smartcontract: allow to use *big.Int
numbers for integers
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
64186d0597
commit
0b0d39f797
7 changed files with 98 additions and 44 deletions
|
@ -5,6 +5,7 @@ package client
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||
|
@ -55,11 +56,11 @@ func (c *Client) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.Pu
|
|||
result, err := c.InvokeFunction(rmHash, "getDesignatedByRole", []smartcontract.Parameter{
|
||||
{
|
||||
Type: smartcontract.IntegerType,
|
||||
Value: int64(role),
|
||||
Value: big.NewInt(int64(role)),
|
||||
},
|
||||
{
|
||||
Type: smartcontract.IntegerType,
|
||||
Value: int64(index),
|
||||
Value: big.NewInt(int64(index)),
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
|
@ -84,7 +85,7 @@ func (c *Client) NNSResolve(nnsHash util.Uint160, name string, typ nns.RecordTyp
|
|||
},
|
||||
{
|
||||
Type: smartcontract.IntegerType,
|
||||
Value: int64(typ),
|
||||
Value: big.NewInt(int64(typ)),
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
|
|
|
@ -15,7 +15,7 @@ func ParameterFromStackItem(i stackitem.Item, seen map[stackitem.Item]bool) Para
|
|||
case *stackitem.BigInteger:
|
||||
return Parameter{
|
||||
Type: IntegerType,
|
||||
Value: i.Value().(*big.Int).Int64(),
|
||||
Value: i.Value().(*big.Int),
|
||||
}
|
||||
case stackitem.Bool:
|
||||
return Parameter{
|
||||
|
|
|
@ -18,7 +18,7 @@ var toContractParameterTestCases = []struct {
|
|||
stackitem.NewBool(true),
|
||||
}),
|
||||
result: Parameter{Type: ArrayType, Value: []Parameter{
|
||||
{Type: IntegerType, Value: int64(1)},
|
||||
{Type: IntegerType, Value: big.NewInt(1)},
|
||||
{Type: BoolType, Value: true},
|
||||
}},
|
||||
},
|
||||
|
@ -37,7 +37,7 @@ var toContractParameterTestCases = []struct {
|
|||
{
|
||||
input: stackitem.NewArray([]stackitem.Item{stackitem.NewBigInteger(big.NewInt(2)), stackitem.NewBool(true)}),
|
||||
result: Parameter{Type: ArrayType, Value: []Parameter{
|
||||
{Type: IntegerType, Value: int64(2)},
|
||||
{Type: IntegerType, Value: big.NewInt(2)},
|
||||
{Type: BoolType, Value: true},
|
||||
}},
|
||||
},
|
||||
|
@ -55,11 +55,11 @@ var toContractParameterTestCases = []struct {
|
|||
Type: MapType,
|
||||
Value: []ParameterPair{
|
||||
{
|
||||
Key: Parameter{Type: IntegerType, Value: int64(1)},
|
||||
Key: Parameter{Type: IntegerType, Value: big.NewInt(1)},
|
||||
Value: Parameter{Type: BoolType, Value: true},
|
||||
}, {
|
||||
Key: Parameter{Type: ByteArrayType, Value: []byte("qwerty")},
|
||||
Value: Parameter{Type: IntegerType, Value: int64(3)},
|
||||
Value: Parameter{Type: IntegerType, Value: big.NewInt(3)},
|
||||
}, {
|
||||
|
||||
Key: Parameter{Type: BoolType, Value: true},
|
||||
|
|
|
@ -5,13 +5,14 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// ParamType represents the Type of the smart contract parameter.
|
||||
|
@ -208,7 +209,11 @@ func adjustValToType(typ ParamType, val string) (interface{}, error) {
|
|||
return nil, errors.New("invalid boolean value")
|
||||
}
|
||||
case IntegerType:
|
||||
return strconv.ParseInt(val, 10, 64)
|
||||
bi, ok := new(big.Int).SetString(val, 10)
|
||||
if !ok || stackitem.CheckIntegerSize(bi) != nil {
|
||||
return nil, errors.New("invalid integer value")
|
||||
}
|
||||
return bi, nil
|
||||
case Hash160Type:
|
||||
u, err := address.StringToUint160(val)
|
||||
if err == nil {
|
||||
|
@ -250,8 +255,8 @@ func adjustValToType(typ ParamType, val string) (interface{}, error) {
|
|||
func inferParamType(val string) ParamType {
|
||||
var err error
|
||||
|
||||
_, err = strconv.Atoi(val)
|
||||
if err == nil {
|
||||
bi, ok := new(big.Int).SetString(val, 10)
|
||||
if ok && stackitem.CheckIntegerSize(bi) == nil {
|
||||
return IntegerType
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@ package smartcontract
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -76,6 +78,7 @@ func TestParseParamType(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInferParamType(t *testing.T) {
|
||||
bi := new(big.Int).Lsh(big.NewInt(1), stackitem.MaxBigIntegerSizeBits-2)
|
||||
var inouts = []struct {
|
||||
in string
|
||||
out ParamType
|
||||
|
@ -88,6 +91,15 @@ func TestInferParamType(t *testing.T) {
|
|||
}, {
|
||||
in: "0",
|
||||
out: IntegerType,
|
||||
}, {
|
||||
in: "8765432187654321111",
|
||||
out: IntegerType,
|
||||
}, {
|
||||
in: bi.String(),
|
||||
out: IntegerType,
|
||||
}, {
|
||||
in: bi.String() + "0", // big for Integer but is still a valid hex
|
||||
out: ByteArrayType,
|
||||
}, {
|
||||
in: "2e10",
|
||||
out: ByteArrayType,
|
||||
|
@ -150,6 +162,8 @@ func TestInferParamType(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAdjustValToType(t *testing.T) {
|
||||
bi := big.NewInt(1).Lsh(big.NewInt(1), stackitem.MaxBigIntegerSizeBits-2)
|
||||
|
||||
var inouts = []struct {
|
||||
typ ParamType
|
||||
val string
|
||||
|
@ -190,15 +204,23 @@ func TestAdjustValToType(t *testing.T) {
|
|||
}, {
|
||||
typ: IntegerType,
|
||||
val: "0",
|
||||
out: int64(0),
|
||||
out: big.NewInt(0),
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: "42",
|
||||
out: int64(42),
|
||||
out: big.NewInt(42),
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: "-42",
|
||||
out: int64(-42),
|
||||
out: big.NewInt(-42),
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: bi.String(),
|
||||
out: bi,
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: bi.String() + "0",
|
||||
err: true,
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: "q",
|
||||
|
|
|
@ -9,15 +9,17 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// Parameter represents a smart contract parameter.
|
||||
|
@ -65,13 +67,12 @@ func (p Parameter) MarshalJSON() ([]byte, error) {
|
|||
case BoolType, StringType, Hash160Type, Hash256Type:
|
||||
resultRawValue, resultErr = json.Marshal(p.Value)
|
||||
case IntegerType:
|
||||
val, ok := p.Value.(int64)
|
||||
val, ok := p.Value.(*big.Int)
|
||||
if !ok {
|
||||
resultErr = errors.New("invalid integer value")
|
||||
break
|
||||
}
|
||||
valStr := strconv.FormatInt(val, 10)
|
||||
resultRawValue = json.RawMessage(`"` + valStr + `"`)
|
||||
resultRawValue = json.RawMessage(`"` + val.String() + `"`)
|
||||
case PublicKeyType, ByteArrayType, SignatureType:
|
||||
if p.Type == PublicKeyType {
|
||||
resultRawValue, resultErr = json.Marshal(hex.EncodeToString(p.Value.([]byte)))
|
||||
|
@ -145,17 +146,22 @@ func (p *Parameter) UnmarshalJSON(data []byte) (err error) {
|
|||
p.Value = s
|
||||
case IntegerType:
|
||||
if err = json.Unmarshal(r.Value, &i); err == nil {
|
||||
p.Value = i
|
||||
p.Value = big.NewInt(i)
|
||||
return
|
||||
}
|
||||
// sometimes integer comes as string
|
||||
if err = json.Unmarshal(r.Value, &s); err != nil {
|
||||
return
|
||||
if jErr := json.Unmarshal(r.Value, &s); jErr != nil {
|
||||
return jErr
|
||||
}
|
||||
if i, err = strconv.ParseInt(s, 10, 64); err != nil {
|
||||
return
|
||||
bi, ok := new(big.Int).SetString(s, 10)
|
||||
if !ok {
|
||||
// In this case previous err should mean string contains non-digit characters.
|
||||
return err
|
||||
}
|
||||
err = stackitem.CheckIntegerSize(bi)
|
||||
if err == nil {
|
||||
p.Value = bi
|
||||
}
|
||||
p.Value = i
|
||||
case ArrayType:
|
||||
// https://github.com/neo-project/neo/blob/3d59ecca5a8deb057bdad94b3028a6d5e25ac088/neo/Network/RPC/RpcServer.cs#L67
|
||||
var rs []Parameter
|
||||
|
@ -205,7 +211,8 @@ func (p *Parameter) EncodeBinary(w *io.BinWriter) {
|
|||
case StringType:
|
||||
w.WriteString(p.Value.(string))
|
||||
case IntegerType:
|
||||
w.WriteU64LE(uint64(p.Value.(int64)))
|
||||
val := p.Value.(*big.Int)
|
||||
w.WriteVarBytes(bigint.ToBytes(val))
|
||||
case ArrayType:
|
||||
w.WriteArray(p.Value.([]Parameter))
|
||||
case MapType:
|
||||
|
@ -236,7 +243,11 @@ func (p *Parameter) DecodeBinary(r *io.BinReader) {
|
|||
case StringType:
|
||||
p.Value = r.ReadString()
|
||||
case IntegerType:
|
||||
p.Value = int64(r.ReadU64LE())
|
||||
bs := r.ReadVarBytes(bigint.MaxBytesLen)
|
||||
if r.Err != nil {
|
||||
return
|
||||
}
|
||||
p.Value = bigint.FromBytes(bs)
|
||||
case ArrayType:
|
||||
ps := []Parameter{}
|
||||
r.ReadArray(&ps)
|
||||
|
@ -318,6 +329,9 @@ func (p Parameter) TryParse(dest interface{}) error {
|
|||
return err
|
||||
}
|
||||
return nil
|
||||
case **big.Int:
|
||||
*dest = bigint.FromBytes(data)
|
||||
return nil
|
||||
case *int64, *int32, *int16, *int8, *int, *uint64, *uint32, *uint16, *uint8, *uint:
|
||||
var size int
|
||||
switch dest.(type) {
|
||||
|
|
|
@ -5,7 +5,9 @@ import (
|
|||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
|
@ -22,9 +24,13 @@ var marshalJSONTestCases = []struct {
|
|||
result string
|
||||
}{
|
||||
{
|
||||
input: Parameter{Type: IntegerType, Value: int64(12345)},
|
||||
input: Parameter{Type: IntegerType, Value: big.NewInt(12345)},
|
||||
result: `{"type":"Integer","value":12345}`,
|
||||
},
|
||||
{
|
||||
input: Parameter{Type: IntegerType, Value: new(big.Int).Lsh(big.NewInt(1), 254)},
|
||||
result: `{"type":"Integer","value":"` + new(big.Int).Lsh(big.NewInt(1), 254).String() + `"}`,
|
||||
},
|
||||
{
|
||||
input: Parameter{Type: StringType, Value: "Some string"},
|
||||
result: `{"type":"String","value":"Some string"}`,
|
||||
|
@ -57,7 +63,7 @@ var marshalJSONTestCases = []struct {
|
|||
Type: ArrayType,
|
||||
Value: []Parameter{
|
||||
{Type: StringType, Value: "str 1"},
|
||||
{Type: IntegerType, Value: int64(2)},
|
||||
{Type: IntegerType, Value: big.NewInt(2)},
|
||||
},
|
||||
},
|
||||
result: `{"type":"Array","value":[{"type":"String","value":"str 1"},{"type":"Integer","value":2}]}`,
|
||||
|
@ -84,7 +90,7 @@ var marshalJSONTestCases = []struct {
|
|||
Value: []ParameterPair{
|
||||
{
|
||||
Key: Parameter{Type: StringType, Value: "key1"},
|
||||
Value: Parameter{Type: IntegerType, Value: int64(1)},
|
||||
Value: Parameter{Type: IntegerType, Value: big.NewInt(1)},
|
||||
},
|
||||
{
|
||||
Key: Parameter{Type: StringType, Value: "key2"},
|
||||
|
@ -102,7 +108,7 @@ var marshalJSONTestCases = []struct {
|
|||
Key: Parameter{Type: StringType, Value: "key1"},
|
||||
Value: Parameter{Type: ArrayType, Value: []Parameter{
|
||||
{Type: StringType, Value: "str 1"},
|
||||
{Type: IntegerType, Value: int64(2)},
|
||||
{Type: IntegerType, Value: big.NewInt(2)},
|
||||
}},
|
||||
},
|
||||
},
|
||||
|
@ -185,11 +191,11 @@ var unmarshalJSONTestCases = []struct {
|
|||
},
|
||||
{
|
||||
input: `{"type":"Integer","value":12345}`,
|
||||
result: Parameter{Type: IntegerType, Value: int64(12345)},
|
||||
result: Parameter{Type: IntegerType, Value: big.NewInt(12345)},
|
||||
},
|
||||
{
|
||||
input: `{"type":"Integer","value":"12345"}`,
|
||||
result: Parameter{Type: IntegerType, Value: int64(12345)},
|
||||
result: Parameter{Type: IntegerType, Value: big.NewInt(12345)},
|
||||
},
|
||||
{
|
||||
input: `{"type":"ByteString","value":"` + hexToBase64("010203") + `"}`,
|
||||
|
@ -215,7 +221,7 @@ var unmarshalJSONTestCases = []struct {
|
|||
Type: ArrayType,
|
||||
Value: []Parameter{
|
||||
{Type: StringType, Value: "str 1"},
|
||||
{Type: IntegerType, Value: int64(2)},
|
||||
{Type: IntegerType, Value: big.NewInt(2)},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -248,7 +254,7 @@ var unmarshalJSONTestCases = []struct {
|
|||
Value: []ParameterPair{
|
||||
{
|
||||
Key: Parameter{Type: StringType, Value: "key1"},
|
||||
Value: Parameter{Type: IntegerType, Value: int64(1)},
|
||||
Value: Parameter{Type: IntegerType, Value: big.NewInt(1)},
|
||||
},
|
||||
{
|
||||
Key: Parameter{Type: StringType, Value: "key2"},
|
||||
|
@ -266,7 +272,7 @@ var unmarshalJSONTestCases = []struct {
|
|||
Key: Parameter{Type: StringType, Value: "key1"},
|
||||
Value: Parameter{Type: ArrayType, Value: []Parameter{
|
||||
{Type: StringType, Value: "str 1"},
|
||||
{Type: IntegerType, Value: int64(2)},
|
||||
{Type: IntegerType, Value: big.NewInt(2)},
|
||||
}},
|
||||
},
|
||||
},
|
||||
|
@ -310,6 +316,8 @@ var unmarshalJSONErrorCases = []string{
|
|||
`{"type": "String","value":1}`, // incorrect Value
|
||||
`{"type": "Integer","value": "nn"}`, // incorrect Integer value
|
||||
`{"type": "Integer","value": []}`, // incorrect Integer value
|
||||
`{"type": "Integer","value":"` +
|
||||
strings.Repeat("9", 100) + `"}`, // too big Integer
|
||||
`{"type": "Array","value": 123}`, // incorrect Array value
|
||||
`{"type": "Hash160","value": "0bcd"}`, // incorrect Uint160 value
|
||||
`{"type": "Hash256","value": "0bcd"}`, // incorrect Uint256 value
|
||||
|
@ -330,7 +338,7 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, input := range unmarshalJSONErrorCases {
|
||||
assert.Error(t, json.Unmarshal([]byte(input), &s))
|
||||
assert.Error(t, json.Unmarshal([]byte(input), &s), input)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,6 +378,10 @@ var tryParseTestCases = []struct {
|
|||
input: []byte{0x63, 0x78, 0x29, 0xcd, 0x0b},
|
||||
expected: int64(50686687331),
|
||||
},
|
||||
{
|
||||
input: []byte{0x63, 0x78, 0x29, 0xcd, 0x0b},
|
||||
expected: big.NewInt(50686687331),
|
||||
},
|
||||
{
|
||||
input: []byte("this is a test string"),
|
||||
expected: "this is a test string",
|
||||
|
@ -450,13 +462,13 @@ func TestNewParameterFromString(t *testing.T) {
|
|||
out: Parameter{StringType, "qwerty"},
|
||||
}, {
|
||||
in: "42",
|
||||
out: Parameter{IntegerType, int64(42)},
|
||||
out: Parameter{IntegerType, big.NewInt(42)},
|
||||
}, {
|
||||
in: "Hello, 世界",
|
||||
out: Parameter{StringType, "Hello, 世界"},
|
||||
}, {
|
||||
in: `\4\2`,
|
||||
out: Parameter{IntegerType, int64(42)},
|
||||
out: Parameter{IntegerType, big.NewInt(42)},
|
||||
}, {
|
||||
in: `\\4\2`,
|
||||
out: Parameter{StringType, `\42`},
|
||||
|
@ -465,7 +477,7 @@ func TestNewParameterFromString(t *testing.T) {
|
|||
out: Parameter{StringType, `\42`},
|
||||
}, {
|
||||
in: "int:42",
|
||||
out: Parameter{IntegerType, int64(42)},
|
||||
out: Parameter{IntegerType, big.NewInt(42)},
|
||||
}, {
|
||||
in: "true",
|
||||
out: Parameter{BoolType, true},
|
||||
|
@ -544,8 +556,8 @@ func TestExpandParameterToEmitable(t *testing.T) {
|
|||
Expected: true,
|
||||
},
|
||||
{
|
||||
In: Parameter{Type: IntegerType, Value: int64(123)},
|
||||
Expected: int64(123),
|
||||
In: Parameter{Type: IntegerType, Value: big.NewInt(123)},
|
||||
Expected: big.NewInt(123),
|
||||
},
|
||||
{
|
||||
In: Parameter{Type: ByteArrayType, Value: []byte{1, 2, 3}},
|
||||
|
@ -575,7 +587,7 @@ func TestExpandParameterToEmitable(t *testing.T) {
|
|||
In: Parameter{Type: ArrayType, Value: []Parameter{
|
||||
{
|
||||
Type: IntegerType,
|
||||
Value: int64(123),
|
||||
Value: big.NewInt(123),
|
||||
},
|
||||
{
|
||||
Type: ByteArrayType,
|
||||
|
@ -591,7 +603,7 @@ func TestExpandParameterToEmitable(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}},
|
||||
Expected: []interface{}{int64(123), []byte{1, 2, 3}, []interface{}{true}},
|
||||
Expected: []interface{}{big.NewInt(123), []byte{1, 2, 3}, []interface{}{true}},
|
||||
},
|
||||
}
|
||||
bw := io.NewBufBinWriter()
|
||||
|
|
Loading…
Reference in a new issue