Merge pull request #3297 from nspcc-dev/sc-convertible
smartcontract: introduce Convertible interface
This commit is contained in:
commit
a19f85ba34
3 changed files with 85 additions and 7 deletions
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,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) {
|
||||
|
|
Loading…
Reference in a new issue