smartcontract: provide interface{}->Parameter conversion
Which is almost like a NeoFS's toStackParameter() on steroids (except it doesn't mess with noderoles package, it can be casted to int). RPC client's Invoke* functions expect Parameters, so make it easy to create them.
This commit is contained in:
parent
a8a2f2ed5a
commit
92a931c145
2 changed files with 260 additions and 0 deletions
|
@ -255,6 +255,101 @@ func NewParameterFromString(in string) (*Parameter, error) {
|
|||
return res, nil
|
||||
}
|
||||
|
||||
// NewParameterFromValue infers Parameter type from the value given and adjusts
|
||||
// the value if needed. It does not copy the value if it can avoid doing so. All
|
||||
// regular integers, util.*, keys.PublicKey*, string and bool types are supported,
|
||||
// slice of byte slices is accepted and converted as well.
|
||||
func NewParameterFromValue(value interface{}) (Parameter, error) {
|
||||
var result = Parameter{
|
||||
Value: value,
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
result.Type = ByteArrayType
|
||||
case string:
|
||||
result.Type = StringType
|
||||
case bool:
|
||||
result.Type = BoolType
|
||||
case *big.Int:
|
||||
result.Type = IntegerType
|
||||
case int8:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(int64(v))
|
||||
case byte:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(int64(v))
|
||||
case int16:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(int64(v))
|
||||
case uint16:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(int64(v))
|
||||
case int32:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(int64(v))
|
||||
case uint32:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(int64(v))
|
||||
case int:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(int64(v))
|
||||
case uint:
|
||||
result.Type = IntegerType
|
||||
result.Value = new(big.Int).SetUint64(uint64(v))
|
||||
case int64:
|
||||
result.Type = IntegerType
|
||||
result.Value = big.NewInt(v)
|
||||
case uint64:
|
||||
result.Type = IntegerType
|
||||
result.Value = new(big.Int).SetUint64(v)
|
||||
case util.Uint160:
|
||||
result.Type = Hash160Type
|
||||
case util.Uint256:
|
||||
result.Type = Hash256Type
|
||||
case keys.PublicKey:
|
||||
return NewParameterFromValue(&v)
|
||||
case *keys.PublicKey:
|
||||
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
|
||||
case []*keys.PublicKey:
|
||||
return NewParameterFromValue(keys.PublicKeys(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
|
||||
case []interface{}:
|
||||
arr := make([]Parameter, 0, len(v))
|
||||
for i := range v {
|
||||
elem, err := NewParameterFromValue(v[i])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
arr = append(arr, elem)
|
||||
}
|
||||
result.Type = ArrayType
|
||||
result.Value = arr
|
||||
default:
|
||||
return result, fmt.Errorf("unsupported parameter %T", value)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -527,3 +527,168 @@ func TestExpandParameterToEmitable(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParameterFromValue(t *testing.T) {
|
||||
pk1, _ := keys.NewPrivateKey()
|
||||
pk2, _ := keys.NewPrivateKey()
|
||||
items := []struct {
|
||||
value interface{}
|
||||
expType ParamType
|
||||
expVal interface{}
|
||||
}{
|
||||
{
|
||||
value: []byte{1, 2, 3},
|
||||
expType: ByteArrayType,
|
||||
expVal: []byte{1, 2, 3},
|
||||
},
|
||||
{
|
||||
value: "hello world",
|
||||
expType: StringType,
|
||||
expVal: "hello world",
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
expType: BoolType,
|
||||
expVal: false,
|
||||
},
|
||||
{
|
||||
value: true,
|
||||
expType: BoolType,
|
||||
expVal: true,
|
||||
},
|
||||
{
|
||||
value: big.NewInt(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: byte(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: int8(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: uint8(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: int16(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: uint16(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: int32(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: uint32(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: 100,
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: uint(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: int64(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: uint64(100),
|
||||
expType: IntegerType,
|
||||
expVal: big.NewInt(100),
|
||||
},
|
||||
{
|
||||
value: util.Uint160{1, 2, 3},
|
||||
expType: Hash160Type,
|
||||
expVal: util.Uint160{1, 2, 3},
|
||||
},
|
||||
{
|
||||
value: util.Uint256{3, 2, 1},
|
||||
expType: Hash256Type,
|
||||
expVal: util.Uint256{3, 2, 1},
|
||||
},
|
||||
{
|
||||
value: pk1.PublicKey(),
|
||||
expType: PublicKeyType,
|
||||
expVal: pk1.PublicKey().Bytes(),
|
||||
},
|
||||
{
|
||||
value: *pk2.PublicKey(),
|
||||
expType: PublicKeyType,
|
||||
expVal: pk2.PublicKey().Bytes(),
|
||||
},
|
||||
{
|
||||
value: [][]byte{{1, 2, 3}, {3, 2, 1}},
|
||||
expType: ArrayType,
|
||||
expVal: []Parameter{{ByteArrayType, []byte{1, 2, 3}}, {ByteArrayType, []byte{3, 2, 1}}},
|
||||
},
|
||||
{
|
||||
value: []*keys.PublicKey{pk1.PublicKey(), pk2.PublicKey()},
|
||||
expType: ArrayType,
|
||||
expVal: []Parameter{{
|
||||
Type: PublicKeyType,
|
||||
Value: pk1.PublicKey().Bytes(),
|
||||
}, {
|
||||
Type: PublicKeyType,
|
||||
Value: pk2.PublicKey().Bytes(),
|
||||
}},
|
||||
},
|
||||
{
|
||||
value: keys.PublicKeys{pk1.PublicKey(), pk2.PublicKey()},
|
||||
expType: ArrayType,
|
||||
expVal: []Parameter{{
|
||||
Type: PublicKeyType,
|
||||
Value: pk1.PublicKey().Bytes(),
|
||||
}, {
|
||||
Type: PublicKeyType,
|
||||
Value: pk2.PublicKey().Bytes(),
|
||||
}},
|
||||
},
|
||||
{
|
||||
value: []interface{}{-42, "random", []byte{1, 2, 3}},
|
||||
expType: ArrayType,
|
||||
expVal: []Parameter{{
|
||||
Type: IntegerType,
|
||||
Value: big.NewInt(-42),
|
||||
}, {
|
||||
Type: StringType,
|
||||
Value: "random",
|
||||
}, {
|
||||
Type: ByteArrayType,
|
||||
Value: []byte{1, 2, 3},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
_, err := NewParameterFromValue(make(map[string]int))
|
||||
require.Error(t, err)
|
||||
_, err = NewParameterFromValue([]interface{}{1, 2, make(map[string]int)})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue