neo-go/pkg/rpc/request/txBuilder.go
Roman Khimov b44f9eefb0 rpc: correctly handle RPC invocations with no arguments, fix #2219
In N3 no arguments passed should be treated as empty arguments array not as
missing array of arguments, because the array must be present even for
functions that accept no parameters.
2021-10-16 21:28:43 +03:00

136 lines
3.4 KiB
Go

package request
import (
"errors"
"fmt"
"strconv"
"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"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
)
// ExpandArrayIntoScript pushes all FuncParam parameters from the given array
// into the given buffer in reverse order.
func ExpandArrayIntoScript(script *io.BinWriter, slice []Param) error {
for j := len(slice) - 1; j >= 0; j-- {
fp, err := slice[j].GetFuncParam()
if err != nil {
return err
}
switch fp.Type {
case smartcontract.ByteArrayType:
str, err := fp.Value.GetBytesBase64()
if err != nil {
return err
}
emit.Bytes(script, str)
case smartcontract.SignatureType:
str, err := fp.Value.GetBytesHex()
if err != nil {
return err
}
emit.Bytes(script, str)
case smartcontract.StringType:
str, err := fp.Value.GetString()
if err != nil {
return err
}
emit.String(script, str)
case smartcontract.Hash160Type:
hash, err := fp.Value.GetUint160FromHex()
if err != nil {
return err
}
emit.Bytes(script, hash.BytesBE())
case smartcontract.Hash256Type:
hash, err := fp.Value.GetUint256()
if err != nil {
return err
}
emit.Bytes(script, hash.BytesBE())
case smartcontract.PublicKeyType:
str, err := fp.Value.GetString()
if err != nil {
return err
}
key, err := keys.NewPublicKeyFromString(string(str))
if err != nil {
return err
}
emit.Bytes(script, key.Bytes())
case smartcontract.IntegerType:
val, err := fp.Value.GetInt()
if err != nil {
return err
}
emit.Int(script, int64(val))
case smartcontract.BoolType:
val, ok := fp.Value.Value.(bool)
if !ok {
return errors.New("not a bool")
}
if val {
emit.Int(script, 1)
} else {
emit.Int(script, 0)
}
case smartcontract.ArrayType:
val, err := fp.Value.GetArray()
if err != nil {
return err
}
err = ExpandArrayIntoScript(script, val)
if err != nil {
return err
}
emit.Int(script, int64(len(val)))
emit.Opcodes(script, opcode.PACK)
default:
return fmt.Errorf("parameter type %v is not supported", fp.Type)
}
}
return nil
}
// CreateFunctionInvocationScript creates a script to invoke given contract with
// given parameters.
func CreateFunctionInvocationScript(contract util.Uint160, method string, params Params) ([]byte, error) {
script := io.NewBufBinWriter()
for i := len(params) - 1; i >= 0; i-- {
switch params[i].Type {
case StringT:
emit.String(script.BinWriter, params[i].String())
case NumberT:
num, err := params[i].GetInt()
if err != nil {
return nil, err
}
emit.String(script.BinWriter, strconv.Itoa(num))
case BooleanT:
val := params[i].GetBoolean()
emit.Bool(script.BinWriter, val)
case ArrayT:
slice, err := params[i].GetArray()
if err != nil {
return nil, err
}
err = ExpandArrayIntoScript(script.BinWriter, slice)
if err != nil {
return nil, err
}
emit.Int(script.BinWriter, int64(len(slice)))
emit.Opcodes(script.BinWriter, opcode.PACK)
}
}
if len(params) == 0 {
emit.Opcodes(script.BinWriter, opcode.NEWARRAY0)
}
emit.AppCallNoArgs(script.BinWriter, contract, method, callflag.All)
return script.Bytes(), nil
}