diff --git a/pkg/compiler/assign_test.go b/pkg/compiler/assign_test.go index ed5b5fe4a..f521d3a4a 100644 --- a/pkg/compiler/assign_test.go +++ b/pkg/compiler/assign_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) var assignTestCases = []testCase{ @@ -144,9 +144,9 @@ func TestManyAssignments(t *testing.T) { src2 := `return a }` - for i := 0; i < vm.MaxArraySize; i++ { + for i := 0; i < stackitem.MaxArraySize; i++ { src1 += "a += 1\n" } - eval(t, src1+src2, big.NewInt(vm.MaxArraySize)) + eval(t, src1+src2, big.NewInt(stackitem.MaxArraySize)) } diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 0be51d22a..2a4b12fa9 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -10,6 +10,10 @@ var syscalls = map[string]map[string]string{ "Next": "System.Enumerator.Next", "Value": "System.Enumerator.Value", }, + "json": { + "Serialize": "System.Json.Serialize", + "Deserialize": "System.Json.Deserialize", + }, "storage": { "ConvertContextToReadOnly": "System.Storage.AsReadOnly", "Delete": "System.Storage.Delete", diff --git a/pkg/core/interop/json/json.go b/pkg/core/interop/json/json.go new file mode 100644 index 000000000..c9f660fa3 --- /dev/null +++ b/pkg/core/interop/json/json.go @@ -0,0 +1,29 @@ +package json + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// Serialize handles System.JSON.Serialize syscall. +func Serialize(_ *interop.Context, v *vm.VM) error { + item := v.Estack().Pop().Item() + data, err := stackitem.ToJSON(item) + if err != nil { + return err + } + v.Estack().PushVal(data) + return nil +} + +// Deserialize handles System.JSON.Deserialize syscall. +func Deserialize(_ *interop.Context, v *vm.VM) error { + data := v.Estack().Pop().Bytes() + item, err := stackitem.FromJSON(data) + if err != nil { + return err + } + v.Estack().PushVal(item) + return nil +} diff --git a/pkg/core/interop/json/json_test.go b/pkg/core/interop/json/json_test.go new file mode 100644 index 000000000..b1d4bb34f --- /dev/null +++ b/pkg/core/interop/json/json_test.go @@ -0,0 +1,68 @@ +package json + +import ( + "encoding/binary" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +var ( + serializeID = emit.InteropNameToID([]byte("System.Json.Serialize")) + deserializeID = emit.InteropNameToID([]byte("System.Json.Deserialize")) +) + +func getInterop(id uint32) *vm.InteropFuncPrice { + switch id { + case serializeID: + return &vm.InteropFuncPrice{ + Func: func(v *vm.VM) error { return Serialize(nil, v) }, + } + case deserializeID: + return &vm.InteropFuncPrice{ + Func: func(v *vm.VM) error { return Deserialize(nil, v) }, + } + default: + return nil + } +} + +func getTestFunc(id uint32, arg interface{}, result interface{}) func(t *testing.T) { + prog := make([]byte, 5) + prog[0] = byte(opcode.SYSCALL) + binary.LittleEndian.PutUint32(prog[1:], id) + + return func(t *testing.T) { + v := vm.New() + v.RegisterInteropGetter(getInterop) + v.LoadScript(prog) + v.Estack().PushVal(arg) + if result == nil { + require.Error(t, v.Run()) + return + } + require.NoError(t, v.Run()) + require.Equal(t, stackitem.Make(result), v.Estack().Pop().Item()) + } +} + +func TestSerialize(t *testing.T) { + t.Run("Serialize", func(t *testing.T) { + t.Run("Good", getTestFunc(serializeID, 42, []byte("42"))) + t.Run("Bad", func(t *testing.T) { + arr := stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), + stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), + }) + getTestFunc(serializeID, arr, nil)(t) + }) + }) + t.Run("Deserialize", func(t *testing.T) { + t.Run("Good", getTestFunc(deserializeID, []byte("42"), 42)) + t.Run("Bad", getTestFunc(deserializeID, []byte("{]"), nil)) + }) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 326b5f3c4..437705ab6 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" + "github.com/nspcc-dev/neo-go/pkg/core/interop/json" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -105,6 +106,8 @@ var systemInterops = []interop.Function{ {Name: "System.Iterator.Key", Func: iterator.Key, Price: 400}, {Name: "System.Iterator.Keys", Func: iterator.Keys, Price: 400}, {Name: "System.Iterator.Values", Func: iterator.Values, Price: 400}, + {Name: "System.Json.Deserialize", Func: json.Deserialize, Price: 500000}, + {Name: "System.Json.Serialize", Func: json.Serialize, Price: 100000}, {Name: "System.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200, RequiredFlags: smartcontract.AllowStates}, {Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 1, AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, diff --git a/pkg/interop/json/json.go b/pkg/interop/json/json.go new file mode 100644 index 000000000..ac9183ca8 --- /dev/null +++ b/pkg/interop/json/json.go @@ -0,0 +1,28 @@ +/* +Package json provides various JSON serialization/deserialization routines. +*/ +package json + +// ToJSON serializes value to json. It uses `System.Json.Serialize` syscall. +// Serialization format is the following: +// []byte -> base64 string +// bool -> json boolean +// nil -> Null +// string -> base64 encoded sequence of underlying bytes +// (u)int* -> integer, only value in -2^53..2^53 are allowed +// []interface{} -> json array +// map[type1]type2 -> json object with string keys marshaled as strings (not base64). +func ToJSON(item interface{}) []byte { + return nil +} + +// FromJSON deserializes value from json. It uses `System.Json.Deserialize` syscall. +// It performs deserialization as follows: +// strings -> []byte (string) from base64 +// integers -> (u)int* types +// null -> interface{}(nil) +// arrays -> []interface{} +// maps -> map[string]interface{} +func FromJSON(data []byte) interface{} { + return nil +} diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 49ba04353..8fa7afd98 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -97,7 +97,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) { err = errNoInstParam } else { var n = binary.LittleEndian.Uint32(c.prog[c.nextip : c.nextip+4]) - if n > MaxItemSize { + if n > stackitem.MaxSize { return instr, nil, errors.New("parameter is too big") } numtoread = int(n) diff --git a/pkg/vm/contract_checks.go b/pkg/vm/contract_checks.go index 9622d2781..1c8bfc4ea 100644 --- a/pkg/vm/contract_checks.go +++ b/pkg/vm/contract_checks.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) var ( @@ -21,14 +22,14 @@ func getNumOfThingsFromInstr(instr opcode.Opcode, param []byte) (int, bool) { nthings = int(instr-opcode.PUSH1) + 1 case instr <= opcode.PUSHINT256: n := bigint.FromBytes(param) - if !n.IsInt64() || n.Int64() > MaxArraySize { + if !n.IsInt64() || n.Int64() > stackitem.MaxArraySize { return 0, false } nthings = int(n.Int64()) default: return 0, false } - if nthings < 1 || nthings > MaxArraySize { + if nthings < 1 || nthings > stackitem.MaxArraySize { return 0, false } return nthings, true @@ -69,7 +70,7 @@ func ParseMultiSigContract(script []byte) (int, [][]byte, bool) { } pubs = append(pubs, param) nkeys++ - if nkeys > MaxArraySize { + if nkeys > stackitem.MaxArraySize { return nsigs, nil, false } } diff --git a/pkg/vm/interop.go b/pkg/vm/interop.go index 7020124bb..f0dd90f35 100644 --- a/pkg/vm/interop.go +++ b/pkg/vm/interop.go @@ -93,7 +93,7 @@ func RuntimeSerialize(vm *VM) error { data, err := stackitem.SerializeItem(item.value) if err != nil { return err - } else if len(data) > MaxItemSize { + } else if len(data) > stackitem.MaxSize { return errors.New("too big item") } diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 07304b037..5d141c7f2 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -18,6 +18,12 @@ import ( // MaxBigIntegerSizeBits is the maximum size of BigInt item in bits. const MaxBigIntegerSizeBits = 32 * 8 +// MaxArraySize is the maximum array size allowed in the VM. +const MaxArraySize = 1024 + +// MaxSize is the maximum item size allowed in the VM. +const MaxSize = 1024 * 1024 + // Item represents the "real" value that is pushed on the stack. type Item interface { fmt.Stringer diff --git a/pkg/vm/stackitem/json.go b/pkg/vm/stackitem/json.go new file mode 100644 index 000000000..81b67f769 --- /dev/null +++ b/pkg/vm/stackitem/json.go @@ -0,0 +1,202 @@ +package stackitem + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + gio "io" + "math" + "math/big" + + "github.com/nspcc-dev/neo-go/pkg/io" +) + +// decoder is a wrapper around json.Decoder helping to mimic C# json decoder behaviour. +type decoder struct { + json.Decoder + + depth int +} + +// MaxAllowedInteger is the maximum integer allowed to be encoded. +const MaxAllowedInteger = 2<<53 - 1 + +// maxJSONDepth is a maximum allowed depth-level of decoded JSON. +const maxJSONDepth = 10 + +// ToJSON encodes Item to JSON. +// It behaves as following: +// ByteArray -> base64 string +// BigInteger -> number +// Bool -> bool +// Null -> null +// Array, Struct -> array +// Map -> map with keys as UTF-8 bytes +func ToJSON(item Item) ([]byte, error) { + buf := io.NewBufBinWriter() + toJSON(buf, item) + if buf.Err != nil { + return nil, buf.Err + } + return buf.Bytes(), nil +} + +func toJSON(buf *io.BufBinWriter, item Item) { + w := buf.BinWriter + if w.Err != nil { + return + } else if buf.Len() > MaxSize { + w.Err = errors.New("item is too big") + } + switch it := item.(type) { + case *Array, *Struct: + w.WriteB('[') + items := it.Value().([]Item) + for i, v := range items { + toJSON(buf, v) + if i < len(items)-1 { + w.WriteB(',') + } + } + w.WriteB(']') + case *Map: + w.WriteB('{') + for i := range it.value { + bs, _ := it.value[i].Key.TryBytes() // map key can always be converted to []byte + w.WriteB('"') + w.WriteBytes(bs) + w.WriteBytes([]byte(`":`)) + toJSON(buf, it.value[i].Value) + if i < len(it.value)-1 { + w.WriteB(',') + } + } + w.WriteB('}') + case *BigInteger: + if it.value.CmpAbs(big.NewInt(MaxAllowedInteger)) == 1 { + w.Err = errors.New("too big integer") + return + } + w.WriteBytes([]byte(it.value.String())) + case *ByteArray: + w.WriteB('"') + val := it.Value().([]byte) + b := make([]byte, base64.StdEncoding.EncodedLen(len(val))) + base64.StdEncoding.Encode(b, val) + w.WriteBytes(b) + w.WriteB('"') + case *Bool: + if it.value { + w.WriteBytes([]byte("true")) + } else { + w.WriteBytes([]byte("false")) + } + case Null: + w.WriteBytes([]byte("null")) + default: + w.Err = fmt.Errorf("invalid item: %s", it.String()) + return + } + if w.Err == nil && buf.Len() > MaxSize { + w.Err = errors.New("item is too big") + } +} + +// FromJSON decodes Item from JSON. +// It behaves as following: +// string -> ByteArray from base64 +// number -> BigInteger +// bool -> Bool +// null -> Null +// array -> Array +// map -> Map, keys are UTF-8 +func FromJSON(data []byte) (Item, error) { + d := decoder{Decoder: *json.NewDecoder(bytes.NewReader(data))} + if item, err := d.decode(); err != nil { + return nil, err + } else if _, err := d.Token(); err != gio.EOF { + return nil, errors.New("unexpected items") + } else { + return item, nil + } +} + +func (d *decoder) decode() (Item, error) { + tok, err := d.Token() + if err != nil { + return nil, err + } + switch t := tok.(type) { + case json.Delim: + switch t { + case json.Delim('{'), json.Delim('['): + if d.depth == maxJSONDepth { + return nil, errors.New("JSON depth limit exceeded") + } + d.depth++ + var item Item + if t == json.Delim('{') { + item, err = d.decodeMap() + } else { + item, err = d.decodeArray() + } + d.depth-- + return item, err + default: + // no error above means corresponding closing token + // was encountered for map or array respectively + return nil, nil + } + case string: + b, err := base64.StdEncoding.DecodeString(t) + if err != nil { + return nil, err + } + return NewByteArray(b), nil + case float64: + if math.Floor(t) != t { + return nil, fmt.Errorf("real value is not allowed: %v", t) + } + return NewBigInteger(big.NewInt(int64(t))), nil + case bool: + return NewBool(t), nil + default: + // it can be only `nil` + return Null{}, nil + } +} + +func (d *decoder) decodeArray() (*Array, error) { + items := []Item{} + for { + item, err := d.decode() + if err != nil { + return nil, err + } + if item == nil { + return NewArray(items), nil + } + items = append(items, item) + } +} + +func (d *decoder) decodeMap() (*Map, error) { + m := NewMap() + for { + key, err := d.Token() + if err != nil { + return nil, err + } + k, ok := key.(string) + if !ok { + return m, nil + } + val, err := d.decode() + if err != nil { + return nil, err + } + m.Add(NewByteArray([]byte(k)), val) + } +} diff --git a/pkg/vm/stackitem/json_test.go b/pkg/vm/stackitem/json_test.go new file mode 100644 index 000000000..1b1ef7718 --- /dev/null +++ b/pkg/vm/stackitem/json_test.go @@ -0,0 +1,105 @@ +package stackitem + +import ( + "encoding/base64" + "math/big" + "testing" + + "github.com/stretchr/testify/require" +) + +func getTestDecodeFunc(js string, expected ...interface{}) func(t *testing.T) { + return func(t *testing.T) { + actual, err := FromJSON([]byte(js)) + if expected[0] == nil { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, Make(expected[0]), actual) + + if len(expected) == 1 { + encoded, err := ToJSON(actual) + require.NoError(t, err) + require.Equal(t, js, string(encoded)) + } + } +} + +func TestFromToJSON(t *testing.T) { + var testBase64 = base64.StdEncoding.EncodeToString([]byte("test")) + t.Run("ByteString", func(t *testing.T) { + t.Run("Empty", getTestDecodeFunc(`""`, []byte{})) + t.Run("Base64", getTestDecodeFunc(`"`+testBase64+`"`, "test")) + }) + t.Run("BigInteger", func(t *testing.T) { + t.Run("ZeroFloat", getTestDecodeFunc(`12.000`, 12, nil)) + t.Run("NonZeroFloat", getTestDecodeFunc(`12.01`, nil)) + t.Run("Negative", getTestDecodeFunc(`-4`, -4)) + t.Run("Positive", getTestDecodeFunc(`123`, 123)) + }) + t.Run("Bool", func(t *testing.T) { + t.Run("True", getTestDecodeFunc(`true`, true)) + t.Run("False", getTestDecodeFunc(`false`, false)) + }) + t.Run("Null", getTestDecodeFunc(`null`, Null{})) + t.Run("Array", func(t *testing.T) { + t.Run("Empty", getTestDecodeFunc(`[]`, NewArray([]Item{}))) + t.Run("Simple", getTestDecodeFunc((`[1,"`+testBase64+`",true,null]`), + NewArray([]Item{NewBigInteger(big.NewInt(1)), NewByteArray([]byte("test")), NewBool(true), Null{}}))) + t.Run("Nested", getTestDecodeFunc(`[[],[{},null]]`, + NewArray([]Item{NewArray([]Item{}), NewArray([]Item{NewMap(), Null{}})}))) + }) + t.Run("Map", func(t *testing.T) { + small := NewMap() + small.Add(NewByteArray([]byte("a")), NewBigInteger(big.NewInt(3))) + large := NewMap() + large.Add(NewByteArray([]byte("3")), small) + large.Add(NewByteArray([]byte("arr")), NewArray([]Item{NewByteArray([]byte("test"))})) + t.Run("Empty", getTestDecodeFunc(`{}`, NewMap())) + t.Run("Small", getTestDecodeFunc(`{"a":3}`, small)) + t.Run("Big", getTestDecodeFunc(`{"3":{"a":3},"arr":["`+testBase64+`"]}`, large)) + }) + t.Run("Invalid", func(t *testing.T) { + t.Run("Empty", getTestDecodeFunc(``, nil)) + t.Run("InvalidString", getTestDecodeFunc(`"not a base64"`, nil)) + t.Run("InvalidArray", getTestDecodeFunc(`[}`, nil)) + t.Run("InvalidMap", getTestDecodeFunc(`{]`, nil)) + t.Run("InvalidMapValue", getTestDecodeFunc(`{"a":{]}`, nil)) + t.Run("AfterArray", getTestDecodeFunc(`[]XX`, nil)) + t.Run("EncodeBigInteger", func(t *testing.T) { + item := NewBigInteger(big.NewInt(MaxAllowedInteger + 1)) + _, err := ToJSON(item) + require.Error(t, err) + }) + t.Run("EncodeInvalidItemType", func(t *testing.T) { + item := NewPointer(1, []byte{1, 2, 3}) + _, err := ToJSON(item) + require.Error(t, err) + }) + t.Run("BigByteArray", func(t *testing.T) { + l := base64.StdEncoding.DecodedLen(MaxSize + 8) + require.True(t, l < MaxSize) // check if test makes sense + item := NewByteArray(make([]byte, l)) + _, err := ToJSON(item) + require.Error(t, err) + }) + t.Run("BigNestedArray", getTestDecodeFunc(`[[[[[[[[[[[]]]]]]]]]]]`, nil)) + t.Run("EncodeRecursive", func(t *testing.T) { + // add this item to speed up test a bit + item := NewByteArray(make([]byte, MaxSize/100)) + t.Run("Array", func(t *testing.T) { + arr := NewArray([]Item{item}) + arr.Append(arr) + _, err := ToJSON(arr) + require.Error(t, err) + }) + t.Run("Map", func(t *testing.T) { + m := NewMap() + m.Add(item, m) + _, err := ToJSON(m) + require.Error(t, err) + }) + }) + }) +} diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 9cb456583..5bd960b62 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -46,12 +46,6 @@ type ScriptHashGetter interface { } const ( - // MaxArraySize is the maximum array size allowed in the VM. - MaxArraySize = 1024 - - // MaxItemSize is the maximum item size allowed in the VM. - MaxItemSize = 1024 * 1024 - // MaxInvocationStackSize is the maximum size of an invocation stack. MaxInvocationStackSize = 1024 @@ -653,7 +647,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.NEWBUFFER: n := toInt(v.estack.Pop().BigInt()) - if n < 0 || n > MaxItemSize { + if n < 0 || n > stackitem.MaxSize { panic("invalid size") } v.estack.PushVal(stackitem.NewBuffer(make([]byte, n))) @@ -684,7 +678,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.CAT: b := v.estack.Pop().Bytes() a := v.estack.Pop().Bytes() - if l := len(a) + len(b); l > MaxItemSize { + if l := len(a) + len(b); l > stackitem.MaxSize { panic(fmt.Sprintf("too big item: %d", l)) } ab := append(a, b...) @@ -1001,7 +995,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.NEWARRAY, opcode.NEWARRAYT: item := v.estack.Pop() n := item.BigInt().Int64() - if n > MaxArraySize { + if n > stackitem.MaxArraySize { panic("too long array") } typ := stackitem.AnyT @@ -1017,7 +1011,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.NEWSTRUCT: item := v.estack.Pop() n := item.BigInt().Int64() - if n > MaxArraySize { + if n > stackitem.MaxArraySize { panic("too long struct") } items := makeArrayOfType(int(n), stackitem.AnyT) @@ -1031,12 +1025,12 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro switch t := arrElem.value.(type) { case *stackitem.Array: - if t.Len() >= MaxArraySize { + if t.Len() >= stackitem.MaxArraySize { panic("too long array") } t.Append(val) case *stackitem.Struct: - if t.Len() >= MaxArraySize { + if t.Len() >= stackitem.MaxArraySize { panic("too long struct") } t.Append(val) @@ -1048,7 +1042,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.PACK: n := int(v.estack.Pop().BigInt().Int64()) - if n < 0 || n > v.estack.Len() || n > MaxArraySize { + if n < 0 || n > v.estack.Len() || n > stackitem.MaxArraySize { panic("OPACK: invalid length") } @@ -1119,7 +1113,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case *stackitem.Map: if i := t.Index(key.value); i >= 0 { v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value) - } else if t.Len() >= MaxArraySize { + } else if t.Len() >= stackitem.MaxArraySize { panic("too big map") } t.Add(key.value, item) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 185848275..83fa9f04c 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -489,9 +489,9 @@ func TestPUSHDATA4(t *testing.T) { } func TestPushData4BigN(t *testing.T) { - prog := make([]byte, 1+4+MaxItemSize+1) + prog := make([]byte, 1+4+stackitem.MaxSize+1) prog[0] = byte(opcode.PUSHDATA4) - binary.LittleEndian.PutUint32(prog[1:], MaxItemSize+1) + binary.LittleEndian.PutUint32(prog[1:], stackitem.MaxSize+1) vm := load(prog) checkVMFailed(t, vm) @@ -1222,7 +1222,7 @@ func TestNEWBUFFER(t *testing.T) { prog := makeProgram(opcode.NEWBUFFER) t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{0, 0, 0}), 3)) t.Run("Negative", getTestFuncForVM(prog, nil, -1)) - t.Run("TooBig", getTestFuncForVM(prog, nil, MaxItemSize+1)) + t.Run("TooBig", getTestFuncForVM(prog, nil, stackitem.MaxSize+1)) } func TestMEMCPY(t *testing.T) { @@ -1251,7 +1251,7 @@ func TestNEWSTRUCT0(t *testing.T) { func TestNEWARRAYArray(t *testing.T) { prog := makeProgram(opcode.NEWARRAY) t.Run("ByteArray", getTestFuncForVM(prog, stackitem.NewArray([]stackitem.Item{}), []byte{})) - t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) + t.Run("BadSize", getTestFuncForVM(prog, nil, stackitem.MaxArraySize+1)) t.Run("Integer", getTestFuncForVM(prog, []stackitem.Item{stackitem.Null{}}, 1)) } @@ -1302,7 +1302,7 @@ func TestNEWARRAYT(t *testing.T) { func TestNEWSTRUCT(t *testing.T) { prog := makeProgram(opcode.NEWSTRUCT) t.Run("ByteArray", getTestFuncForVM(prog, stackitem.NewStruct([]stackitem.Item{}), []byte{})) - t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) + t.Run("BadSize", getTestFuncForVM(prog, nil, stackitem.MaxArraySize+1)) t.Run("Integer", getTestFuncForVM(prog, stackitem.NewStruct([]stackitem.Item{stackitem.Null{}}), 1)) } @@ -1330,15 +1330,15 @@ func TestAPPENDBad(t *testing.T) { func TestAPPENDGoodSizeLimit(t *testing.T) { prog := makeProgram(opcode.NEWARRAY, opcode.DUP, opcode.PUSH0, opcode.APPEND) vm := load(prog) - vm.estack.PushVal(MaxArraySize - 1) + vm.estack.PushVal(stackitem.MaxArraySize - 1) runVM(t, vm) assert.Equal(t, 1, vm.estack.Len()) - assert.Equal(t, MaxArraySize, len(vm.estack.Pop().Array())) + assert.Equal(t, stackitem.MaxArraySize, len(vm.estack.Pop().Array())) } func TestAPPENDBadSizeLimit(t *testing.T) { prog := makeProgram(opcode.NEWARRAY, opcode.DUP, opcode.PUSH0, opcode.APPEND) - runWithArgs(t, prog, nil, MaxArraySize) + runWithArgs(t, prog, nil, stackitem.MaxArraySize) } func TestPICKITEM(t *testing.T) { @@ -1399,11 +1399,11 @@ func TestSETITEMMap(t *testing.T) { func TestSETITEMBigMapBad(t *testing.T) { prog := makeProgram(opcode.SETITEM) m := stackitem.NewMap() - for i := 0; i < MaxArraySize; i++ { + for i := 0; i < stackitem.MaxArraySize; i++ { m.Add(stackitem.Make(i), stackitem.Make(i)) } - runWithArgs(t, prog, nil, m, MaxArraySize, 0) + runWithArgs(t, prog, nil, m, stackitem.MaxArraySize, 0) } // This test checks is SETITEM properly updates reference counter. @@ -1411,7 +1411,7 @@ func TestSETITEMBigMapBad(t *testing.T) { // 2. SETITEM each of them to a map. // 3. Replace each of them with a scalar value. func TestSETITEMMapStackLimit(t *testing.T) { - size := MaxArraySize - 3 + size := stackitem.MaxArraySize - 3 m := stackitem.NewMap() m.Add(stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) m.Add(stackitem.NewBigInteger(big.NewInt(2)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) @@ -1431,7 +1431,7 @@ func TestSETITEMBigMapGood(t *testing.T) { vm := load(prog) m := stackitem.NewMap() - for i := 0; i < MaxArraySize; i++ { + for i := 0; i < stackitem.MaxArraySize; i++ { m.Add(stackitem.Make(i), stackitem.Make(i)) } vm.estack.Push(&Element{value: m}) @@ -1842,7 +1842,7 @@ func TestCAT(t *testing.T) { t.Run("NoArgument", getTestFuncForVM(prog, nil)) t.Run("OneArgument", getTestFuncForVM(prog, nil, []byte("abc"))) t.Run("BigItem", func(t *testing.T) { - arg := make([]byte, MaxItemSize/2+1) + arg := make([]byte, stackitem.MaxSize/2+1) runWithArgs(t, prog, nil, arg, arg) }) t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("abcdef")), []byte("abc"), []byte("def"))) @@ -1900,10 +1900,10 @@ func TestPACK(t *testing.T) { func TestPACKBigLen(t *testing.T) { prog := makeProgram(opcode.PACK) vm := load(prog) - for i := 0; i <= MaxArraySize; i++ { + for i := 0; i <= stackitem.MaxArraySize; i++ { vm.estack.PushVal(0) } - vm.estack.PushVal(MaxArraySize + 1) + vm.estack.PushVal(stackitem.MaxArraySize + 1) checkVMFailed(t, vm) }