diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index 5c870add7..d961b191e 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -14,6 +14,7 @@ import ( "strings" "unicode/utf8" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" ) @@ -437,3 +438,28 @@ func NewParameterFromString(in string) (*Parameter, error) { } return res, nil } + +// ExpandParameterToEmitable converts parameter to a type which can be handled as +// an array item by emit.Array. It correlates with the way RPC server handles +// FuncParams for invoke* calls inside the request.ExpandArrayIntoScript function. +func ExpandParameterToEmitable(param Parameter) (interface{}, error) { + var err error + switch t := param.Type; t { + case PublicKeyType: + return param.Value.(*keys.PublicKey).Bytes(), nil + case ArrayType: + arr := param.Value.([]Parameter) + res := make([]interface{}, len(arr)) + for i := range arr { + res[i], err = ExpandParameterToEmitable(arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + case MapType, InteropInterfaceType, UnknownType, AnyType, VoidType: + return nil, fmt.Errorf("unsupported parameter type: %s", t.String()) + default: + return param.Value, nil + } +} diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index 45b421568..021f9ca9d 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -9,7 +9,10 @@ import ( "testing" "github.com/nspcc-dev/neo-go/internal/testserdes" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -517,3 +520,85 @@ func hexToBase64(s string) string { b, _ := hex.DecodeString(s) return base64.StdEncoding.EncodeToString(b) } + +func TestExpandParameterToEmitable(t *testing.T) { + pk, _ := keys.NewPrivateKey() + testCases := []struct { + In Parameter + Expected interface{} + }{ + { + In: Parameter{Type: BoolType, Value: true}, + Expected: true, + }, + { + In: Parameter{Type: IntegerType, Value: int64(123)}, + Expected: int64(123), + }, + { + In: Parameter{Type: ByteArrayType, Value: []byte{1, 2, 3}}, + Expected: []byte{1, 2, 3}, + }, + { + In: Parameter{Type: StringType, Value: "writing's on the wall"}, + Expected: "writing's on the wall", + }, + { + In: Parameter{Type: Hash160Type, Value: util.Uint160{1, 2, 3}}, + Expected: util.Uint160{1, 2, 3}, + }, + { + In: Parameter{Type: Hash256Type, Value: util.Uint256{1, 2, 3}}, + Expected: util.Uint256{1, 2, 3}, + }, + { + In: Parameter{Type: PublicKeyType, Value: pk.PublicKey()}, + Expected: pk.PublicKey().Bytes(), + }, + { + In: Parameter{Type: SignatureType, Value: []byte{1, 2, 3}}, + Expected: []byte{1, 2, 3}, + }, + { + In: Parameter{Type: ArrayType, Value: []Parameter{ + { + Type: IntegerType, + Value: int64(123), + }, + { + Type: ByteArrayType, + Value: []byte{1, 2, 3}, + }, + { + Type: ArrayType, + Value: []Parameter{ + { + Type: BoolType, + Value: true, + }, + }, + }, + }}, + Expected: []interface{}{int64(123), []byte{1, 2, 3}, []interface{}{true}}, + }, + } + bw := io.NewBufBinWriter() + for _, testCase := range testCases { + actual, err := ExpandParameterToEmitable(testCase.In) + require.NoError(t, err) + require.Equal(t, testCase.Expected, actual) + + emit.Array(bw.BinWriter, actual) + require.NoError(t, bw.Err) + } + errCases := []Parameter{ + {Type: AnyType}, + {Type: UnknownType}, + {Type: MapType}, + {Type: InteropInterfaceType}, + } + for _, errCase := range errCases { + _, err := ExpandParameterToEmitable(errCase) + require.Error(t, err) + } +}