rpc: pass single Param to CreateFunctionInvocationScript
N3 contracts require method and array of arguments to function correctly, but CreateFunctionInvocationScript can produce scripts that won't work correctly if one is to pass anything but a single array parameter to it. This doesn't make much sense, so we can always expect there to be an array of parameters in the third positional invokefunction argument (the same way C# node does).
This commit is contained in:
parent
d01f9da8f3
commit
5b470f14cc
3 changed files with 46 additions and 49 deletions
|
@ -3,7 +3,6 @@ package request
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -103,28 +102,19 @@ func ExpandArrayIntoScript(script *io.BinWriter, slice []Param) error {
|
||||||
|
|
||||||
// CreateFunctionInvocationScript creates a script to invoke given contract with
|
// CreateFunctionInvocationScript creates a script to invoke given contract with
|
||||||
// given parameters.
|
// given parameters.
|
||||||
func CreateFunctionInvocationScript(contract util.Uint160, method string, params Params) ([]byte, error) {
|
func CreateFunctionInvocationScript(contract util.Uint160, method string, param *Param) ([]byte, error) {
|
||||||
script := io.NewBufBinWriter()
|
script := io.NewBufBinWriter()
|
||||||
for i := len(params) - 1; i >= 0; i-- {
|
if param == nil {
|
||||||
if slice, err := params[i].GetArray(); err == nil {
|
|
||||||
err = ExpandArrayIntoScript(script.BinWriter, slice)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
emit.Int(script.BinWriter, int64(len(slice)))
|
|
||||||
emit.Opcodes(script.BinWriter, opcode.PACK)
|
|
||||||
} else if s, err := params[i].GetStringStrict(); err == nil {
|
|
||||||
emit.String(script.BinWriter, s)
|
|
||||||
} else if n, err := params[i].GetIntStrict(); err == nil {
|
|
||||||
emit.String(script.BinWriter, strconv.Itoa(n))
|
|
||||||
} else if b, err := params[i].GetBooleanStrict(); err == nil {
|
|
||||||
emit.Bool(script.BinWriter, b)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("failed to convert parmeter %s to script parameter", params[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(params) == 0 {
|
|
||||||
emit.Opcodes(script.BinWriter, opcode.NEWARRAY0)
|
emit.Opcodes(script.BinWriter, opcode.NEWARRAY0)
|
||||||
|
} else if slice, err := param.GetArray(); err == nil {
|
||||||
|
err = ExpandArrayIntoScript(script.BinWriter, slice)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
emit.Int(script.BinWriter, int64(len(slice)))
|
||||||
|
emit.Opcodes(script.BinWriter, opcode.PACK)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("failed to convert %s to script parameter", param)
|
||||||
}
|
}
|
||||||
|
|
||||||
emit.AppCallNoArgs(script.BinWriter, contract, method, callflag.All)
|
emit.AppCallNoArgs(script.BinWriter, contract, method, callflag.All)
|
||||||
|
|
|
@ -26,9 +26,6 @@ func TestInvocationScriptCreationGood(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
ps: Params{{RawMessage: []byte(`42`)}},
|
ps: Params{{RawMessage: []byte(`42`)}},
|
||||||
script: "c21f0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
|
script: "c21f0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
|
||||||
}, {
|
|
||||||
ps: Params{{RawMessage: []byte(`"m"`)}, {RawMessage: []byte(`true`)}},
|
|
||||||
script: "11db201f0c016d0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
|
|
||||||
}, {
|
}, {
|
||||||
ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[]`)}},
|
ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[]`)}},
|
||||||
script: "10c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
|
script: "10c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
|
||||||
|
@ -72,7 +69,11 @@ func TestInvocationScriptCreationGood(t *testing.T) {
|
||||||
for i, ps := range paramScripts {
|
for i, ps := range paramScripts {
|
||||||
method, err := ps.ps[0].GetString()
|
method, err := ps.ps[0].GetString()
|
||||||
require.NoError(t, err, fmt.Sprintf("testcase #%d", i))
|
require.NoError(t, err, fmt.Sprintf("testcase #%d", i))
|
||||||
script, err := CreateFunctionInvocationScript(contract, method, ps.ps[1:])
|
var p *Param
|
||||||
|
if len(ps.ps) > 1 {
|
||||||
|
p = &ps.ps[1]
|
||||||
|
}
|
||||||
|
script, err := CreateFunctionInvocationScript(contract, method, p)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, ps.script, hex.EncodeToString(script), fmt.Sprintf("testcase #%d", i))
|
assert.Equal(t, ps.script, hex.EncodeToString(script), fmt.Sprintf("testcase #%d", i))
|
||||||
}
|
}
|
||||||
|
@ -81,18 +82,19 @@ func TestInvocationScriptCreationGood(t *testing.T) {
|
||||||
func TestInvocationScriptCreationBad(t *testing.T) {
|
func TestInvocationScriptCreationBad(t *testing.T) {
|
||||||
contract := util.Uint160{}
|
contract := util.Uint160{}
|
||||||
|
|
||||||
var testParams = []Params{
|
var testParams = []Param{
|
||||||
{{RawMessage: []byte(`[{"type": "ByteArray", "value": "qwerty"}]`)}},
|
{RawMessage: []byte(`true`)},
|
||||||
{{RawMessage: []byte(`[{"type": "Signature", "value": "qwerty"}]`)}},
|
{RawMessage: []byte(`[{"type": "ByteArray", "value": "qwerty"}]`)},
|
||||||
{{RawMessage: []byte(`[{"type": "Hash160", "value": "qwerty"}]`)}},
|
{RawMessage: []byte(`[{"type": "Signature", "value": "qwerty"}]`)},
|
||||||
{{RawMessage: []byte(`[{"type": "Hash256", "value": "qwerty"}]`)}},
|
{RawMessage: []byte(`[{"type": "Hash160", "value": "qwerty"}]`)},
|
||||||
{{RawMessage: []byte(`[{"type": "PublicKey", "value": 42}]`)}},
|
{RawMessage: []byte(`[{"type": "Hash256", "value": "qwerty"}]`)},
|
||||||
{{RawMessage: []byte(`[{"type": "PublicKey", "value": "qwerty"}]`)}},
|
{RawMessage: []byte(`[{"type": "PublicKey", "value": 42}]`)},
|
||||||
{{RawMessage: []byte(`[{"type": "Integer", "value": "123q"}]`)}},
|
{RawMessage: []byte(`[{"type": "PublicKey", "value": "qwerty"}]`)},
|
||||||
{{RawMessage: []byte(`[{"type": "Unknown"}]`)}},
|
{RawMessage: []byte(`[{"type": "Integer", "value": "123q"}]`)},
|
||||||
|
{RawMessage: []byte(`[{"type": "Unknown"}]`)},
|
||||||
}
|
}
|
||||||
for i, ps := range testParams {
|
for i, ps := range testParams {
|
||||||
_, err := CreateFunctionInvocationScript(contract, "", ps)
|
_, err := CreateFunctionInvocationScript(contract, "", &ps)
|
||||||
assert.NotNil(t, err, fmt.Sprintf("testcase #%d", i))
|
assert.NotNil(t, err, fmt.Sprintf("testcase #%d", i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1554,28 +1554,33 @@ func (s *Server) getCommittee(_ request.Params) (interface{}, *response.Error) {
|
||||||
|
|
||||||
// invokeFunction implements the `invokeFunction` RPC call.
|
// invokeFunction implements the `invokeFunction` RPC call.
|
||||||
func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *response.Error) {
|
func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *response.Error) {
|
||||||
|
if len(reqParams) < 2 {
|
||||||
|
return nil, response.ErrInvalidParams
|
||||||
|
}
|
||||||
scriptHash, responseErr := s.contractScriptHashFromParam(reqParams.Value(0))
|
scriptHash, responseErr := s.contractScriptHashFromParam(reqParams.Value(0))
|
||||||
if responseErr != nil {
|
if responseErr != nil {
|
||||||
return nil, responseErr
|
return nil, responseErr
|
||||||
}
|
}
|
||||||
tx := &transaction.Transaction{}
|
|
||||||
checkWitnessHashesIndex := len(reqParams)
|
|
||||||
if checkWitnessHashesIndex > 3 {
|
|
||||||
signers, _, err := reqParams[3].GetSignersWithWitnesses()
|
|
||||||
if err != nil {
|
|
||||||
return nil, response.ErrInvalidParams
|
|
||||||
}
|
|
||||||
tx.Signers = signers
|
|
||||||
checkWitnessHashesIndex--
|
|
||||||
}
|
|
||||||
if len(tx.Signers) == 0 {
|
|
||||||
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}}
|
|
||||||
}
|
|
||||||
method, err := reqParams[1].GetString()
|
method, err := reqParams[1].GetString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, response.ErrInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
script, err := request.CreateFunctionInvocationScript(scriptHash, method, reqParams[2:checkWitnessHashesIndex])
|
var params *request.Param
|
||||||
|
if len(reqParams) > 2 {
|
||||||
|
params = &reqParams[2]
|
||||||
|
}
|
||||||
|
tx := &transaction.Transaction{}
|
||||||
|
if len(reqParams) > 3 {
|
||||||
|
signers, _, err := reqParams[3].GetSignersWithWitnesses()
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.ErrInvalidParams
|
||||||
|
}
|
||||||
|
tx.Signers = signers
|
||||||
|
}
|
||||||
|
if len(tx.Signers) == 0 {
|
||||||
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}}
|
||||||
|
}
|
||||||
|
script, err := request.CreateFunctionInvocationScript(scriptHash, method, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, response.NewInternalServerError("can't create invocation script", err)
|
return nil, response.NewInternalServerError("can't create invocation script", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue