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
|
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
|
// 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
|
// an array item by emit.Array. It correlates with the way an RPC server handles
|
||||||
// FuncParams for invoke* calls inside the request.ExpandArrayIntoScript function.
|
// FuncParams for invoke* calls inside the request.ExpandArrayIntoScript function.
|
||||||
|
|
|
@ -527,3 +527,168 @@ func TestExpandParameterToEmitable(t *testing.T) {
|
||||||
require.Error(t, err)
|
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