Skip to content

Commit

Permalink
Merge pull request #3297 from nspcc-dev/sc-convertible
Browse files Browse the repository at this point in the history
smartcontract: introduce Convertible interface
  • Loading branch information
roman-khimov committed Jan 25, 2024
2 parents ef99a7a + 161b83f commit a19f85b
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 7 deletions.
14 changes: 14 additions & 0 deletions pkg/rpcclient/nns/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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.
Expand All @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions pkg/smartcontract/parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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:
Expand Down
67 changes: 60 additions & 7 deletions pkg/smartcontract/parameter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"math"
"math/big"
"strings"
Expand Down Expand Up @@ -554,13 +555,33 @@ 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()
items := []struct {
value any
expType ParamType
expVal any
err string // expected error substring
}{
{
value: []byte{1, 2, 3},
Expand Down Expand Up @@ -741,20 +762,52 @@ 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",
},
{
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 {
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))
require.Error(t, err)
_, err = NewParameterFromValue([]any{1, 2, make(map[string]int)})
require.Error(t, err)
}

func TestParametersFromValues(t *testing.T) {
Expand Down

0 comments on commit a19f85b

Please sign in to comment.