From 5b470f14ccc39718ff821577e1aca7419481996f Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sat, 20 Nov 2021 19:25:42 +0300 Subject: [PATCH] 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). --- pkg/rpc/request/txBuilder.go | 32 ++++++++++------------------- pkg/rpc/request/tx_builder_test.go | 30 ++++++++++++++------------- pkg/rpc/server/server.go | 33 +++++++++++++++++------------- 3 files changed, 46 insertions(+), 49 deletions(-) diff --git a/pkg/rpc/request/txBuilder.go b/pkg/rpc/request/txBuilder.go index c61a8f4c7..4a18af412 100644 --- a/pkg/rpc/request/txBuilder.go +++ b/pkg/rpc/request/txBuilder.go @@ -3,7 +3,6 @@ package request import ( "errors" "fmt" - "strconv" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "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 // 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() - for i := len(params) - 1; i >= 0; i-- { - 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 { + if param == nil { 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) diff --git a/pkg/rpc/request/tx_builder_test.go b/pkg/rpc/request/tx_builder_test.go index 52a36627b..1cc837fe6 100644 --- a/pkg/rpc/request/tx_builder_test.go +++ b/pkg/rpc/request/tx_builder_test.go @@ -26,9 +26,6 @@ func TestInvocationScriptCreationGood(t *testing.T) { }, { ps: Params{{RawMessage: []byte(`42`)}}, script: "c21f0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", - }, { - ps: Params{{RawMessage: []byte(`"m"`)}, {RawMessage: []byte(`true`)}}, - script: "11db201f0c016d0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[]`)}}, script: "10c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", @@ -72,7 +69,11 @@ func TestInvocationScriptCreationGood(t *testing.T) { for i, ps := range paramScripts { method, err := ps.ps[0].GetString() 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.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) { contract := util.Uint160{} - var testParams = []Params{ - {{RawMessage: []byte(`[{"type": "ByteArray", "value": "qwerty"}]`)}}, - {{RawMessage: []byte(`[{"type": "Signature", "value": "qwerty"}]`)}}, - {{RawMessage: []byte(`[{"type": "Hash160", "value": "qwerty"}]`)}}, - {{RawMessage: []byte(`[{"type": "Hash256", "value": "qwerty"}]`)}}, - {{RawMessage: []byte(`[{"type": "PublicKey", "value": 42}]`)}}, - {{RawMessage: []byte(`[{"type": "PublicKey", "value": "qwerty"}]`)}}, - {{RawMessage: []byte(`[{"type": "Integer", "value": "123q"}]`)}}, - {{RawMessage: []byte(`[{"type": "Unknown"}]`)}}, + var testParams = []Param{ + {RawMessage: []byte(`true`)}, + {RawMessage: []byte(`[{"type": "ByteArray", "value": "qwerty"}]`)}, + {RawMessage: []byte(`[{"type": "Signature", "value": "qwerty"}]`)}, + {RawMessage: []byte(`[{"type": "Hash160", "value": "qwerty"}]`)}, + {RawMessage: []byte(`[{"type": "Hash256", "value": "qwerty"}]`)}, + {RawMessage: []byte(`[{"type": "PublicKey", "value": 42}]`)}, + {RawMessage: []byte(`[{"type": "PublicKey", "value": "qwerty"}]`)}, + {RawMessage: []byte(`[{"type": "Integer", "value": "123q"}]`)}, + {RawMessage: []byte(`[{"type": "Unknown"}]`)}, } for i, ps := range testParams { - _, err := CreateFunctionInvocationScript(contract, "", ps) + _, err := CreateFunctionInvocationScript(contract, "", &ps) assert.NotNil(t, err, fmt.Sprintf("testcase #%d", i)) } } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 81bf5f6ca..eea14c92d 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -1554,28 +1554,33 @@ func (s *Server) getCommittee(_ request.Params) (interface{}, *response.Error) { // invokeFunction implements the `invokeFunction` RPC call. 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)) if responseErr != nil { 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() if err != nil { 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 { return nil, response.NewInternalServerError("can't create invocation script", err) }