From f80f453933f5b9b985ec28236932972b472cb4c3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 11 Sep 2024 16:20:08 +0300 Subject: [PATCH 1/2] smartcontract: use generics to simplify slice handling It's all the same in its essence. Signed-off-by: Roman Khimov --- pkg/smartcontract/parameter.go | 45 ++++++++++++++-------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index 0b1bb96ab9..3bb88b0709 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -343,35 +343,16 @@ func NewParameterFromValue(value any) (Parameter, error) { result.Type = PublicKeyType result.Value = v.Bytes() case [][]byte: - arr := make([]Parameter, 0, len(v)) - for i := range v { - // We know the type exactly, so error is not possible. - elem, _ := NewParameterFromValue(v[i]) - arr = append(arr, elem) - } - result.Type = ArrayType - result.Value = arr + return newArrayParameter(v) case []Parameter: result.Type = ArrayType result.Value = slices.Clone(v) case []*keys.PublicKey: - return NewParameterFromValue(keys.PublicKeys(v)) + return newArrayParameter(v) case keys.PublicKeys: - arr := make([]Parameter, 0, len(v)) - for i := range v { - // We know the type exactly, so error is not possible. - elem, _ := NewParameterFromValue(v[i]) - arr = append(arr, elem) - } - result.Type = ArrayType - result.Value = arr + return newArrayParameter(v) case []any: - arr, err := NewParametersFromValues(v...) - if err != nil { - return result, err - } - result.Type = ArrayType - result.Value = arr + return newArrayParameter(v) case nil: result.Type = AnyType default: @@ -381,9 +362,15 @@ func NewParameterFromValue(value any) (Parameter, error) { return result, nil } -// NewParametersFromValues is similar to NewParameterFromValue except that it -// works with multiple values and returns a simple slice of Parameter. -func NewParametersFromValues(values ...any) ([]Parameter, error) { +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 { elem, err := NewParameterFromValue(values[i]) @@ -395,6 +382,12 @@ func NewParametersFromValues(values ...any) ([]Parameter, error) { return res, nil } +// NewParametersFromValues is similar to NewParameterFromValue except that it +// works with multiple values and returns a simple slice of Parameter. +func NewParametersFromValues(values ...any) ([]Parameter, error) { + return newArrayOfParameters(values) +} + // ExpandParameterToEmitable converts a parameter to a type which can be handled as // an array item by emit.Array. It correlates with the way an RPC server handles // FuncParams for invoke* calls inside the request.ExpandArrayIntoScript function. From 8469f97d09beccf40682377cb051a5282fd98488 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 11 Sep 2024 16:45:17 +0300 Subject: [PATCH 2/2] smartcontract: extend slice types accepted by NewParameterFromValue It's _very_ annoying to not be able to use []string types properly: test invocation failed: unsupported parameter []string But the same thing can happen to any other slice, so accept slices of every basic type we accept above. Reorder tests to match implementation switch as well. Signed-off-by: Roman Khimov --- pkg/smartcontract/parameter.go | 42 +++++++++- pkg/smartcontract/parameter_test.go | 121 +++++++++++++++++++++++++--- 2 files changed, 151 insertions(+), 12 deletions(-) diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index 3bb88b0709..a7a61e906a 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -342,11 +342,49 @@ func NewParameterFromValue(value any) (Parameter, error) { case *keys.PublicKey: result.Type = PublicKeyType result.Value = v.Bytes() - case [][]byte: - return newArrayParameter(v) 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: diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index d9cd889dff..865dd79190 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -673,6 +673,11 @@ func TestParameterFromValue(t *testing.T) { expType: ByteArrayType, expVal: []byte{1, 2, 3}, }, + { + value: testConvertible{i: 123}, + expType: IntegerType, + expVal: 123, + }, { value: util.Uint160{1, 2, 3}, expType: Hash160Type, @@ -712,9 +717,9 @@ func TestParameterFromValue(t *testing.T) { expVal: pk2.PublicKey().Bytes(), }, { - value: nil, - expType: AnyType, - expVal: nil, + value: []Parameter{{ByteArrayType, []byte{1, 2, 3}}, {ByteArrayType, []byte{3, 2, 1}}}, + expType: ArrayType, + expVal: []Parameter{{ByteArrayType, []byte{1, 2, 3}}, {ByteArrayType, []byte{3, 2, 1}}}, }, { value: [][]byte{{1, 2, 3}, {3, 2, 1}}, @@ -722,12 +727,97 @@ func TestParameterFromValue(t *testing.T) { expVal: []Parameter{{ByteArrayType, []byte{1, 2, 3}}, {ByteArrayType, []byte{3, 2, 1}}}, }, { - value: []Parameter{{ByteArrayType, []byte{1, 2, 3}}, {ByteArrayType, []byte{3, 2, 1}}}, + value: []string{"qwe", "asd"}, + expType: ArrayType, + expVal: []Parameter{{StringType, "qwe"}, {StringType, "asd"}}, + }, + { + value: []bool{false, true}, + expType: ArrayType, + expVal: []Parameter{{BoolType, false}, {BoolType, true}}, + }, + { + value: []*big.Int{big.NewInt(100), big.NewInt(42)}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []int8{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []int16{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []uint16{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []int32{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []uint32{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []int{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []uint{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []int64{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []uint64{100, 42}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, big.NewInt(100)}, {IntegerType, big.NewInt(42)}}, + }, + { + value: []*Parameter{{ByteArrayType, []byte{1, 2, 3}}, {ByteArrayType, []byte{3, 2, 1}}}, expType: ArrayType, expVal: []Parameter{{ByteArrayType, []byte{1, 2, 3}}, {ByteArrayType, []byte{3, 2, 1}}}, }, { - value: []*keys.PublicKey{pk1.PublicKey(), pk2.PublicKey()}, + value: []Convertible{testConvertible{i: 123}, testConvertible{i: 321}}, + expType: ArrayType, + expVal: []Parameter{{IntegerType, 123}, {IntegerType, 321}}, + }, + { + value: []util.Uint160{{1, 2, 3}, {3, 2, 1}}, + expType: ArrayType, + expVal: []Parameter{{Hash160Type, util.Uint160{1, 2, 3}}, {Hash160Type, util.Uint160{3, 2, 1}}}, + }, + { + value: []util.Uint256{{1, 2, 3}, {3, 2, 1}}, + expType: ArrayType, + expVal: []Parameter{{Hash256Type, util.Uint256{1, 2, 3}}, {Hash256Type, util.Uint256{3, 2, 1}}}, + }, + { + value: []*util.Uint160{{1, 2, 3}, nil, {3, 2, 1}}, + expType: ArrayType, + expVal: []Parameter{{Hash160Type, util.Uint160{1, 2, 3}}, {AnyType, nil}, {Hash160Type, util.Uint160{3, 2, 1}}}, + }, + { + value: []*util.Uint256{{1, 2, 3}, nil, {3, 2, 1}}, + expType: ArrayType, + expVal: []Parameter{{Hash256Type, util.Uint256{1, 2, 3}}, {AnyType, nil}, {Hash256Type, util.Uint256{3, 2, 1}}}, + }, + { + value: []keys.PublicKey{*pk1.PublicKey(), *pk2.PublicKey()}, expType: ArrayType, expVal: []Parameter{{ Type: PublicKeyType, @@ -748,6 +838,17 @@ func TestParameterFromValue(t *testing.T) { Value: pk2.PublicKey().Bytes(), }}, }, + { + value: []*keys.PublicKey{pk1.PublicKey(), pk2.PublicKey()}, + expType: ArrayType, + expVal: []Parameter{{ + Type: PublicKeyType, + Value: pk1.PublicKey().Bytes(), + }, { + Type: PublicKeyType, + Value: pk2.PublicKey().Bytes(), + }}, + }, { value: []any{-42, "random", []byte{1, 2, 3}}, expType: ArrayType, @@ -762,11 +863,6 @@ func TestParameterFromValue(t *testing.T) { Value: []byte{1, 2, 3}, }}, }, - { - value: testConvertible{i: 123}, - expType: IntegerType, - expVal: 123, - }, { value: []any{1, testConvertible{i: 123}}, expType: ArrayType, @@ -781,6 +877,11 @@ func TestParameterFromValue(t *testing.T) { }, }, }, + { + value: nil, + expType: AnyType, + expVal: nil, + }, { value: testConvertible{err: "invalid i value"}, err: "invalid i value",