diff --git a/pkg/io/size.go b/pkg/io/size.go index 8bfabb5fa5..f4636e9013 100644 --- a/pkg/io/size.go +++ b/pkg/io/size.go @@ -73,7 +73,7 @@ func GetVarSize(value any) int { valueSize := 0 if valueLength != 0 { - switch reflect.ValueOf(value).Index(0).Interface().(type) { + switch v.Index(0).Interface().(type) { case Serializable: for i := range valueLength { valueSize += GetVarSize(v.Index(i).Interface()) diff --git a/pkg/rpcclient/invoker/invoker_test.go b/pkg/rpcclient/invoker/invoker_test.go index 39b21bcafa..699a99fabc 100644 --- a/pkg/rpcclient/invoker/invoker_test.go +++ b/pkg/rpcclient/invoker/invoker_test.go @@ -83,17 +83,17 @@ func TestInvoker(t *testing.T) { require.NoError(t, err) require.Equal(t, resExp, res) - _, err = inv.Verify(util.Uint160{}, nil, make(map[int]int)) + _, err = inv.Verify(util.Uint160{}, nil, make(chan struct{})) require.Error(t, err) - _, err = inv.Call(util.Uint160{}, "method", make(map[int]int)) + _, err = inv.Call(util.Uint160{}, "method", make(chan struct{})) require.Error(t, err) res, err = inv.CallAndExpandIterator(util.Uint160{}, "method", 10, 42) require.NoError(t, err) require.Equal(t, resExp, res) - _, err = inv.CallAndExpandIterator(util.Uint160{}, "method", 10, make(map[int]int)) + _, err = inv.CallAndExpandIterator(util.Uint160{}, "method", 10, make(chan struct{})) require.Error(t, err) } t.Run("standard", func(t *testing.T) { diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index a7a61e906a..c24c09c759 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" "os" + "reflect" "slices" "strings" "unicode/utf8" @@ -265,7 +266,7 @@ func NewParameterFromString(in string) (*Parameter, error) { // NewParameterFromValue infers Parameter type from the value given and adjusts // the value if needed. It does not copy the value if it can avoid doing so. All // regular integers, util.*, keys.PublicKey*, string and bool types are supported, -// slice of byte slices is accepted and converted as well. [errors.ErrUnsupported] +// slices of various kinds are converted as well. [errors.ErrUnsupported] // will be returned for types that can't be used now. func NewParameterFromValue(value any) (Parameter, error) { var result = Parameter{ @@ -345,69 +346,46 @@ func NewParameterFromValue(value any) (Parameter, error) { case []Parameter: result.Type = ArrayType result.Value = slices.Clone(v) - case [][]byte: - return newArrayParameter(v) - case []string: - return newArrayParameter(v) - case []bool: - return newArrayParameter(v) - case []*big.Int: - return newArrayParameter(v) - case []int8: - return newArrayParameter(v) - case []int16: - return newArrayParameter(v) - case []uint16: - return newArrayParameter(v) - case []int32: - return newArrayParameter(v) - case []uint32: - return newArrayParameter(v) - case []int: - return newArrayParameter(v) - case []uint: - return newArrayParameter(v) - case []int64: - return newArrayParameter(v) - case []uint64: - return newArrayParameter(v) - case []*Parameter: - return newArrayParameter(v) - case []Convertible: - return newArrayParameter(v) - case []util.Uint160: - return newArrayParameter(v) - case []util.Uint256: - return newArrayParameter(v) - case []*util.Uint160: - return newArrayParameter(v) - case []*util.Uint256: - return newArrayParameter(v) - case []keys.PublicKey: - return newArrayParameter(v) - case []*keys.PublicKey: - return newArrayParameter(v) - case keys.PublicKeys: - return newArrayParameter(v) - case []any: - return newArrayParameter(v) case nil: result.Type = AnyType default: - return result, fmt.Errorf("%w: %T type", errors.ErrUnsupported, value) + rv := reflect.ValueOf(value) + switch rv.Kind() { + case reflect.Slice, reflect.Array: + res := make([]Parameter, 0, rv.Len()) + for i := range rv.Len() { + elem, err := NewParameterFromValue(rv.Index(i).Interface()) + if err != nil { + return result, fmt.Errorf("array index %d: %w", i, err) + } + res = append(res, elem) + } + result.Type = ArrayType + result.Value = res + case reflect.Map: + res := make([]ParameterPair, 0, rv.Len()) + iter := rv.MapRange() + for iter.Next() { + k, err := NewParameterFromValue(iter.Key().Interface()) + if err != nil { + return result, fmt.Errorf("map key: %w", err) + } + v, err := NewParameterFromValue(iter.Value().Interface()) + if err != nil { + return result, fmt.Errorf("map value: %w", err) + } + res = append(res, ParameterPair{Key: k, Value: v}) + } + result.Type = MapType + result.Value = res + default: + return result, fmt.Errorf("%w: %T type", errors.ErrUnsupported, value) + } } return result, nil } -func newArrayParameter[E any, S ~[]E](values S) (Parameter, error) { - arr, err := newArrayOfParameters(values) - if err != nil { - return Parameter{}, err - } - return Parameter{Type: ArrayType, Value: arr}, nil -} - func newArrayOfParameters[E any, S ~[]E](values S) ([]Parameter, error) { res := make([]Parameter, 0, len(values)) for i := range values { diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index 865dd79190..7afb7b3f4c 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -887,12 +887,58 @@ func TestParameterFromValue(t *testing.T) { err: "invalid i value", }, { - value: make(map[string]int), - err: "unsupported operation: map[string]int type", + value: make(map[string]int), + expType: MapType, + expVal: []ParameterPair{}, }, { - value: []any{1, 2, make(map[string]int)}, - err: "unsupported operation: map[string]int type", + value: make(map[string]int), + expType: MapType, + expVal: []ParameterPair{}, + }, + { + value: map[string]string{"key": "value"}, + expType: MapType, + expVal: []ParameterPair{{ + Key: Parameter{ + Type: StringType, + Value: "key", + }, + Value: Parameter{ + Type: StringType, + Value: "value", + }, + }}, + }, + { + value: map[int]int64{42: 100500}, + expType: MapType, + expVal: []ParameterPair{{ + Key: Parameter{ + Type: IntegerType, + Value: big.NewInt(42), + }, + Value: Parameter{ + Type: IntegerType, + Value: big.NewInt(100500), + }, + }}, + }, + { + value: make(chan int), + err: "unsupported operation: chan int type", + }, + { + value: []any{1, 2, make(chan int)}, + err: "unsupported operation: chan int type", + }, + { + value: map[string]chan int{"aaa": make(chan int)}, + err: "unsupported operation: chan int type", + }, + { + value: map[error]string{errors.New("some"): "value"}, + err: "unsupported operation: *errors.errorString type", }, } @@ -924,6 +970,6 @@ func TestParametersFromValues(t *testing.T) { Type: ByteArrayType, Value: []byte{3, 2, 1}, }}, res) - _, err = NewParametersFromValues(42, make(map[int]int), []byte{3, 2, 1}) + _, err = NewParametersFromValues(42, make(chan int), []byte{3, 2, 1}) require.Error(t, err) }