From b90c0ece87970f09382efc9b2f0c81e13ed7cf1d Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 Jan 2024 14:47:07 +0300 Subject: [PATCH 1/3] smartcontract: introduce Convertible interface Allow to pass Convertible to NewParameterFromValue. Close #3285. Signed-off-by: Anna Shaleva --- pkg/smartcontract/parameter.go | 11 ++++++ pkg/smartcontract/parameter_test.go | 55 +++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index 758b86bd1..24577d854 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -25,6 +25,11 @@ type Parameter struct { Value any `json:"value"` } +// Convertible is something that can be converted to Parameter. +type Convertible interface { + ToSCParameter() (Parameter, error) +} + // ParameterPair represents a key-value pair, a slice of which is stored in // MapType Parameter. type ParameterPair struct { @@ -307,6 +312,12 @@ func NewParameterFromValue(value any) (Parameter, error) { result = *v case Parameter: result = v + case Convertible: + var err error + result, err = v.ToSCParameter() + if err != nil { + return result, fmt.Errorf("failed to convert smartcontract.Convertible (%T) to Parameter: %w", v, err) + } case util.Uint160: result.Type = Hash160Type case util.Uint256: diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index c93179a2a..a4353a50f 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" + "errors" "math" "math/big" "strings" @@ -554,6 +555,25 @@ func TestExpandParameterToEmitableToStackitem(t *testing.T) { } } +// testConvertible implements Convertible interface and needed for NewParameterFromValue +// test. +type testConvertible struct { + i int + err string +} + +var _ = Convertible(testConvertible{}) + +func (c testConvertible) ToSCParameter() (Parameter, error) { + if c.err != "" { + return Parameter{}, errors.New(c.err) + } + return Parameter{ + Type: IntegerType, + Value: c.i, + }, nil +} + func TestParameterFromValue(t *testing.T) { pk1, _ := keys.NewPrivateKey() pk2, _ := keys.NewPrivateKey() @@ -561,6 +581,7 @@ func TestParameterFromValue(t *testing.T) { value any expType ParamType expVal any + err string // expected error substring }{ { value: []byte{1, 2, 3}, @@ -741,14 +762,42 @@ 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, + expVal: []Parameter{ + { + Type: IntegerType, + Value: big.NewInt(1), + }, + { + Type: IntegerType, + Value: 123, + }, + }, + }, + { + value: testConvertible{err: "invalid i value"}, + err: "invalid i value", + }, } for _, item := range items { t.Run(item.expType.String()+" to stack parameter", func(t *testing.T) { res, err := NewParameterFromValue(item.value) - require.NoError(t, err) - require.Equal(t, item.expType, res.Type) - require.Equal(t, item.expVal, res.Value) + if item.err != "" { + require.Error(t, err) + require.Contains(t, err.Error(), item.err) + } else { + require.NoError(t, err) + require.Equal(t, item.expType, res.Type) + require.Equal(t, item.expVal, res.Value) + } }) } _, err := NewParameterFromValue(make(map[string]int)) From a22a7177e33c9838a57faea25a3eb4a3b432a916 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 Jan 2024 16:06:14 +0300 Subject: [PATCH 2/3] smartcontract: improve TestParameterFromValue Run error cases in the same loop with test cases. No functional changes. Signed-off-by: Anna Shaleva --- pkg/smartcontract/parameter_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index a4353a50f..1413b246e 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -785,6 +785,14 @@ func TestParameterFromValue(t *testing.T) { value: testConvertible{err: "invalid i value"}, err: "invalid i value", }, + { + value: make(map[string]int), + err: "unsupported parameter map[string]int", + }, + { + value: []any{1, 2, make(map[string]int)}, + err: "unsupported parameter map[string]int", + }, } for _, item := range items { @@ -800,10 +808,6 @@ func TestParameterFromValue(t *testing.T) { } }) } - _, err := NewParameterFromValue(make(map[string]int)) - require.Error(t, err) - _, err = NewParameterFromValue([]any{1, 2, make(map[string]int)}) - require.Error(t, err) } func TestParametersFromValues(t *testing.T) { From 161b83fd7f70ea0ce727f39ab2469ae9eb444d4e Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 Jan 2024 14:50:26 +0300 Subject: [PATCH 3/3] rpcclient: ensure nns.RecordType can be passed as a parameter to invoker Implement smartcontract.Convertible for nns.RecordType. Signed-off-by: Anna Shaleva --- pkg/rpcclient/nns/record.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkg/rpcclient/nns/record.go b/pkg/rpcclient/nns/record.go index da017f9ab..17ad4b40d 100644 --- a/pkg/rpcclient/nns/record.go +++ b/pkg/rpcclient/nns/record.go @@ -3,7 +3,9 @@ package nns import ( "errors" "fmt" + "math/big" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -17,6 +19,10 @@ type RecordState struct { // RecordType is domain name service record types. type RecordType byte +// Ensure RecordType implements smartcontract.Convertible for proper handling as +// a parameter to invoker.Invoker methods. +var _ = smartcontract.Convertible(RecordType(0)) + // Record types are defined in [RFC 1035](https://tools.ietf.org/html/rfc1035) const ( // A represents address record type. @@ -33,6 +39,14 @@ const ( AAAA RecordType = 28 ) +// ToSCParameter implements smartcontract.Convertible interface. +func (r RecordType) ToSCParameter() (smartcontract.Parameter, error) { + return smartcontract.Parameter{ + Type: smartcontract.IntegerType, + Value: big.NewInt(int64(r)), + }, nil +} + // FromStackItem fills RecordState with data from the given stack item if it can // be correctly converted to RecordState. func (r *RecordState) FromStackItem(itm stackitem.Item) error {