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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +19,10 @@ type RecordState struct {
|
||||||
// RecordType is domain name service record types.
|
// RecordType is domain name service record types.
|
||||||
type RecordType byte
|
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)
|
// Record types are defined in [RFC 1035](https://tools.ietf.org/html/rfc1035)
|
||||||
const (
|
const (
|
||||||
// A represents address record type.
|
// A represents address record type.
|
||||||
|
@ -33,6 +39,14 @@ const (
|
||||||
AAAA RecordType = 28
|
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
|
// FromStackItem fills RecordState with data from the given stack item if it can
|
||||||
// be correctly converted to RecordState.
|
// be correctly converted to RecordState.
|
||||||
func (r *RecordState) FromStackItem(itm stackitem.Item) error {
|
func (r *RecordState) FromStackItem(itm stackitem.Item) error {
|
||||||
|
|
|
@ -25,6 +25,11 @@ type Parameter struct {
|
||||||
Value any `json:"value"`
|
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
|
// ParameterPair represents a key-value pair, a slice of which is stored in
|
||||||
// MapType Parameter.
|
// MapType Parameter.
|
||||||
type ParameterPair struct {
|
type ParameterPair struct {
|
||||||
|
@ -307,6 +312,12 @@ func NewParameterFromValue(value any) (Parameter, error) {
|
||||||
result = *v
|
result = *v
|
||||||
case Parameter:
|
case Parameter:
|
||||||
result = v
|
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:
|
case util.Uint160:
|
||||||
result.Type = Hash160Type
|
result.Type = Hash160Type
|
||||||
case util.Uint256:
|
case util.Uint256:
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"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) {
|
func TestParameterFromValue(t *testing.T) {
|
||||||
pk1, _ := keys.NewPrivateKey()
|
pk1, _ := keys.NewPrivateKey()
|
||||||
pk2, _ := keys.NewPrivateKey()
|
pk2, _ := keys.NewPrivateKey()
|
||||||
|
@ -561,6 +581,7 @@ func TestParameterFromValue(t *testing.T) {
|
||||||
value any
|
value any
|
||||||
expType ParamType
|
expType ParamType
|
||||||
expVal any
|
expVal any
|
||||||
|
err string // expected error substring
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
value: []byte{1, 2, 3},
|
value: []byte{1, 2, 3},
|
||||||
|
@ -741,20 +762,52 @@ func TestParameterFromValue(t *testing.T) {
|
||||||
Value: []byte{1, 2, 3},
|
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 {
|
for _, item := range items {
|
||||||
t.Run(item.expType.String()+" to stack parameter", func(t *testing.T) {
|
t.Run(item.expType.String()+" to stack parameter", func(t *testing.T) {
|
||||||
res, err := NewParameterFromValue(item.value)
|
res, err := NewParameterFromValue(item.value)
|
||||||
require.NoError(t, err)
|
if item.err != "" {
|
||||||
require.Equal(t, item.expType, res.Type)
|
require.Error(t, err)
|
||||||
require.Equal(t, item.expVal, res.Value)
|
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) {
|
func TestParametersFromValues(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue