Merge pull request #394 from nspcc-dev/fix/json-tests-bugs

VM: fix some bugs from neo-vm JSON tests
This commit is contained in:
Roman Khimov 2019-09-16 19:46:12 +03:00 committed by GitHub
commit 0838948540
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 323 additions and 64 deletions

View file

@ -121,6 +121,8 @@ func (e *Element) Array() []StackItem {
switch t := e.value.(type) {
case *ArrayItem:
return t.value
case *StructItem:
return t.value
default:
panic("element is not an array")
}

View file

@ -26,7 +26,7 @@ var binaryExprTestCases = []testCase{
return x
}
`,
big.NewInt(0),
[]byte{},
},
{
"simple div",
@ -86,7 +86,7 @@ var binaryExprTestCases = []testCase{
return 0
}
`,
big.NewInt(0),
[]byte{},
},
{
"compare equal strings with eql",
@ -128,7 +128,7 @@ var binaryExprTestCases = []testCase{
return 0
}
`,
big.NewInt(0),
[]byte{},
},
{
"compare equal ints with eql",
@ -156,7 +156,7 @@ var binaryExprTestCases = []testCase{
return 0
}
`,
big.NewInt(0),
[]byte{},
},
{
"compare not equal ints with eql",
@ -170,7 +170,7 @@ var binaryExprTestCases = []testCase{
return 0
}
`,
big.NewInt(0),
[]byte{},
},
{
"compare not equal ints with neq",

View file

@ -275,7 +275,7 @@ func TestIfUnaryInvert(t *testing.T) {
return 0
}
`
eval(t, src, big.NewInt(0))
eval(t, src, []byte{})
}
func TestAppendByte(t *testing.T) {

View file

@ -35,7 +35,7 @@ func TestNotAssignedFunctionCall(t *testing.T) {
return 0
}
`
eval(t, src, big.NewInt(0))
eval(t, src, []byte{})
}
func TestMultipleFunctionCalls(t *testing.T) {

View file

@ -30,7 +30,7 @@ func TestGT(t *testing.T) {
return 0
}
`
eval(t, src, big.NewInt(0))
eval(t, src, []byte{})
}
func TestGTE(t *testing.T) {
@ -44,7 +44,7 @@ func TestGTE(t *testing.T) {
return 0
}
`
eval(t, src, big.NewInt(0))
eval(t, src, []byte{})
}
func TestLAND(t *testing.T) {
@ -89,5 +89,5 @@ func TestNestedIF(t *testing.T) {
return 0
}
`
eval(t, src, big.NewInt(0))
eval(t, src, []byte{})
}

View file

@ -32,7 +32,7 @@ func TestImportStruct(t *testing.T) {
return b.Y
}
`
eval(t, src, big.NewInt(0))
eval(t, src, []byte{})
}
func TestMultipleDirFileImport(t *testing.T) {

View file

@ -179,7 +179,7 @@ var structTestCases = []testCase{
return t.y
}
`,
big.NewInt(0),
[]byte{},
},
{
"test return struct from func",
@ -209,7 +209,7 @@ var structTestCases = []testCase{
vm.NewBigIntegerItem(1),
vm.NewBigIntegerItem(2),
vm.NewByteArrayItem([]byte("hello")),
vm.NewBigIntegerItem(0),
vm.NewByteArrayItem([]byte{}),
},
},
{

View file

@ -257,7 +257,7 @@ func (v *VM) execute(ctx *Context, op Instruction) {
v.estack.PushVal(val)
case PUSH0:
v.estack.PushVal(0)
v.estack.PushVal([]byte{})
case PUSHDATA1:
n := ctx.readByte()
@ -375,7 +375,7 @@ func (v *VM) execute(ctx *Context, op Instruction) {
case XTUCK:
n := int(v.estack.Pop().BigInt().Int64())
if n < 0 {
if n <= 0 {
panic("XTUCK: invalid length")
}
a := v.estack.Dup(0)
@ -388,21 +388,20 @@ func (v *VM) execute(ctx *Context, op Instruction) {
v.estack.InsertAt(a, n)
case ROT:
c := v.estack.Pop()
b := v.estack.Pop()
a := v.estack.Pop()
v.estack.Push(b)
v.estack.Push(c)
v.estack.Push(a)
e := v.estack.RemoveAt(2)
if e == nil {
panic("no top-level element found")
}
v.estack.Push(e)
case DEPTH:
v.estack.PushVal(v.estack.Len())
case NIP:
elem := v.estack.Pop()
_ = v.estack.Pop()
v.estack.Push(elem)
elem := v.estack.RemoveAt(1)
if elem == nil {
panic("no second element found")
}
case OVER:
b := v.estack.Pop()
@ -441,11 +440,20 @@ func (v *VM) execute(ctx *Context, op Instruction) {
}
case DROP:
if v.estack.Len() < 1 {
panic("stack is too small")
}
v.estack.Pop()
case EQUAL:
b := v.estack.Pop()
if b == nil {
panic("no top-level element found")
}
a := v.estack.Pop()
if a == nil {
panic("no second-to-the-top element found")
}
v.estack.PushVal(reflect.DeepEqual(a, b))
// Bit operations.
@ -497,11 +505,17 @@ func (v *VM) execute(ctx *Context, op Instruction) {
case SHL:
b := v.estack.Pop().BigInt()
if b.Int64() == 0 {
return
}
a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Lsh(a, uint(b.Int64())))
case SHR:
b := v.estack.Pop().BigInt()
if b.Int64() == 0 {
return
}
a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Rsh(a, uint(b.Int64())))
@ -601,31 +615,27 @@ func (v *VM) execute(ctx *Context, op Instruction) {
case NEWARRAY:
item := v.estack.Pop()
switch t := item.value.(type) {
case *BigIntegerItem:
n := t.value.Int64()
items := make([]StackItem, n)
v.estack.PushVal(&ArrayItem{items})
case *StructItem:
v.estack.PushVal(&ArrayItem{t.value})
case *ArrayItem:
v.estack.PushVal(t)
default:
panic("NEWARRAY: invalid operand")
n := item.BigInt()
items := makeArrayOfFalses(int(n.Int64()))
v.estack.PushVal(&ArrayItem{items})
}
case NEWSTRUCT:
item := v.estack.Pop()
switch t := item.value.(type) {
case *BigIntegerItem:
n := t.value.Int64()
items := make([]StackItem, n)
v.estack.PushVal(&StructItem{items})
case *ArrayItem:
v.estack.PushVal(&StructItem{t.value})
case *StructItem:
v.estack.PushVal(t)
default:
panic("NEWSTRUCT: invalid operand")
n := item.BigInt()
items := makeArrayOfFalses(int(n.Int64()))
v.estack.PushVal(&StructItem{items})
}
case APPEND:
@ -683,7 +693,12 @@ func (v *VM) execute(ctx *Context, op Instruction) {
item := arr[index]
v.estack.PushVal(item)
default:
panic("PICKITEM: unknown type")
arr := obj.Bytes()
if index < 0 || index >= len(arr) {
panic("PICKITEM: invalid index")
}
item := arr[index]
v.estack.PushVal(int(item))
}
case SETITEM:
@ -707,7 +722,7 @@ func (v *VM) execute(ctx *Context, op Instruction) {
}
case REVERSE:
a := v.estack.Peek(0).Array()
a := v.estack.Pop().Array()
if len(a) > 1 {
for i, j := 0, len(a)-1; i <= j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
@ -715,10 +730,25 @@ func (v *VM) execute(ctx *Context, op Instruction) {
}
case REMOVE:
key := int(v.estack.Pop().BigInt().Int64())
elem := v.estack.Peek(0)
a := elem.Array()
elem := v.estack.Pop()
switch t := elem.value.(type) {
case *ArrayItem:
a := t.value
if key < 0 || key >= len(a) {
panic("REMOVE: invalid index")
}
a = append(a[:key], a[key+1:]...)
elem.value = makeStackItem(a)
t.value = a
case *StructItem:
a := t.value
if key < 0 || key >= len(a) {
panic("REMOVE: invalid index")
}
a = append(a[:key], a[key+1:]...)
t.value = a
default:
panic("REMOVE: invalid type")
}
case ARRAYSIZE:
elem := v.estack.Pop()
@ -735,10 +765,7 @@ func (v *VM) execute(ctx *Context, op Instruction) {
case SIZE:
elem := v.estack.Pop()
arr, ok := elem.value.Value().([]uint8)
if !ok {
panic("SIZE: item not of type []uint8")
}
arr := elem.Bytes()
v.estack.PushVal(len(arr))
case JMP, JMPIF, JMPIFNOT:
@ -840,6 +867,14 @@ func (v *VM) execute(ctx *Context, op Instruction) {
}
}
func makeArrayOfFalses(n int) []StackItem {
items := make([]StackItem, n)
for i := range items {
items[i] = &BoolItem{false}
}
return items
}
func init() {
log.SetPrefix("NEO-GO-VM > ")
log.SetFlags(0)

View file

@ -283,6 +283,50 @@ func TestSub(t *testing.T) {
assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64())
}
func TestSHRGood(t *testing.T) {
prog := makeProgram(SHR)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func TestSHRZero(t *testing.T) {
prog := makeProgram(SHR)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
vm.estack.PushVal(0)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
}
func TestSHLGood(t *testing.T) {
prog := makeProgram(SHL)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(16), vm.estack.Pop().value)
}
func TestSHLZero(t *testing.T) {
prog := makeProgram(SHL)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
vm.estack.PushVal(0)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
}
func TestLT(t *testing.T) {
prog := makeProgram(LT)
vm := load(prog)
@ -335,6 +379,32 @@ func TestDepth(t *testing.T) {
assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64())
}
func TestEQUALNoArguments(t *testing.T) {
prog := makeProgram(EQUAL)
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestEQUALBad1Argument(t *testing.T) {
prog := makeProgram(EQUAL)
vm := load(prog)
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestEQUALGoodInteger(t *testing.T) {
prog := makeProgram(EQUAL)
vm := load(prog)
vm.estack.PushVal(5)
vm.estack.PushVal(5)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestNumEqual(t *testing.T) {
prog := makeProgram(NUMEQUAL)
vm := load(prog)
@ -371,7 +441,7 @@ func TestNEWARRAYInteger(t *testing.T) {
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{make([]StackItem, 1)}, vm.estack.Pop().value)
assert.Equal(t, &ArrayItem{[]StackItem{makeStackItem(false)}}, vm.estack.Pop().value)
}
func TestNEWARRAYStruct(t *testing.T) {
@ -396,12 +466,14 @@ func TestNEWARRAYArray(t *testing.T) {
assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value)
}
func TestNEWARRAYWrongType(t *testing.T) {
func TestNEWARRAYByteArray(t *testing.T) {
prog := makeProgram(NEWARRAY)
vm := load(prog)
vm.estack.Push(NewElement([]byte{}))
vm.estack.PushVal([]byte{})
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{[]StackItem{}}, vm.estack.Pop().value)
}
func TestNEWSTRUCTInteger(t *testing.T) {
@ -411,7 +483,7 @@ func TestNEWSTRUCTInteger(t *testing.T) {
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &StructItem{make([]StackItem, 1)}, vm.estack.Pop().value)
assert.Equal(t, &StructItem{[]StackItem{makeStackItem(false)}}, vm.estack.Pop().value)
}
func TestNEWSTRUCTArray(t *testing.T) {
@ -436,12 +508,14 @@ func TestNEWSTRUCTStruct(t *testing.T) {
assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value)
}
func TestNEWSTRUCTWrongType(t *testing.T) {
func TestNEWSTRUCTByteArray(t *testing.T) {
prog := makeProgram(NEWSTRUCT)
vm := load(prog)
vm.estack.Push(NewElement([]byte{}))
vm.estack.PushVal([]byte{})
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &StructItem{[]StackItem{}}, vm.estack.Pop().value)
}
func TestAPPENDArray(t *testing.T) {
@ -488,6 +562,64 @@ func TestAPPENDWrongType(t *testing.T) {
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPICKITEMBadIndex(t *testing.T) {
prog := makeProgram(PICKITEM)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
vm.estack.PushVal(0)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPICKITEMArray(t *testing.T) {
prog := makeProgram(PICKITEM)
vm := load(prog)
vm.estack.PushVal([]StackItem{makeStackItem(1), makeStackItem(2)})
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestPICKITEMByteArray(t *testing.T) {
prog := makeProgram(PICKITEM)
vm := load(prog)
vm.estack.PushVal([]byte{1, 2})
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestSIZENoArgument(t *testing.T) {
prog := makeProgram(SIZE)
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestSIZEByteArray(t *testing.T) {
prog := makeProgram(SIZE)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestSIZEBool(t *testing.T) {
prog := makeProgram(SIZE)
vm := load(prog)
vm.estack.PushVal(false)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func TestSIGNNoArgument(t *testing.T) {
prog := makeProgram(SIGN)
vm := load(prog)
@ -633,6 +765,29 @@ func TestPICKgood(t *testing.T) {
assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64())
}
func TestROTBad(t *testing.T) {
prog := makeProgram(ROT)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestROTGood(t *testing.T) {
prog := makeProgram(ROT)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 3, vm.estack.Len())
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(3), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestXTUCKbadNoitem(t *testing.T) {
prog := makeProgram(XTUCK)
vm := load(prog)
@ -658,6 +813,15 @@ func TestXTUCKbadNegative(t *testing.T) {
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestXTUCKbadZero(t *testing.T) {
prog := makeProgram(XTUCK)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(0)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestXTUCKgood(t *testing.T) {
prog := makeProgram(XTUCK)
topelement := 5
@ -745,6 +909,41 @@ func TestOVERgood(t *testing.T) {
assert.Equal(t, 3, vm.estack.Len())
}
func TestNIPBadNoItem(t *testing.T) {
prog := makeProgram(NIP)
vm := load(prog)
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestNIPGood(t *testing.T) {
prog := makeProgram(NIP)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestDROPBadNoItem(t *testing.T) {
prog := makeProgram(DROP)
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestDROPGood(t *testing.T) {
prog := makeProgram(DROP)
vm := load(prog)
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 0, vm.estack.Len())
}
func TestXDROPbadNoitem(t *testing.T) {
prog := makeProgram(XDROP)
vm := load(prog)
@ -1118,7 +1317,7 @@ func TestREVERSEBadNotArray(t *testing.T) {
}
func TestREVERSEGoodOneElem(t *testing.T) {
prog := makeProgram(REVERSE)
prog := makeProgram(DUP, REVERSE)
elements := []int{22}
vm := load(prog)
vm.estack.PushVal(1)
@ -1132,13 +1331,42 @@ func TestREVERSEGoodOneElem(t *testing.T) {
assert.Equal(t, int64(elements[0]), e.Int64())
}
func TestREVERSEGoodStruct(t *testing.T) {
eodd := []int{22, 34, 42, 55, 81}
even := []int{22, 34, 42, 55, 81, 99}
eall := [][]int{eodd, even}
for _, elements := range eall {
prog := makeProgram(DUP, REVERSE)
vm := load(prog)
vm.estack.PushVal(1)
arr := make([]StackItem, len(elements))
for i := range elements {
arr[i] = makeStackItem(elements[i])
}
vm.estack.Push(&Element{value: &StructItem{arr}})
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 2, vm.estack.Len())
a := vm.estack.Peek(0).Array()
assert.Equal(t, len(elements), len(a))
for k, v := range elements {
e := a[len(a)-1-k].Value().(*big.Int)
assert.Equal(t, int64(v), e.Int64())
}
assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64())
}
}
func TestREVERSEGood(t *testing.T) {
eodd := []int{22, 34, 42, 55, 81}
even := []int{22, 34, 42, 55, 81, 99}
eall := [][]int{eodd, even}
for _, elements := range eall {
prog := makeProgram(REVERSE)
prog := makeProgram(DUP, REVERSE)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(elements)
@ -1190,23 +1418,17 @@ func TestREMOVEBadIndex(t *testing.T) {
}
func TestREMOVEGood(t *testing.T) {
prog := makeProgram(REMOVE)
prog := makeProgram(DUP, PUSH2, REMOVE)
elements := []int{22, 34, 42, 55, 81}
reselements := []int{22, 34, 55, 81}
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(elements)
vm.estack.PushVal(2)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 2, vm.estack.Len())
a := vm.estack.Peek(0).Array()
assert.Equal(t, len(reselements), len(a))
for k, v := range reselements {
e := a[k].Value().(*big.Int)
assert.Equal(t, int64(v), e.Int64())
}
assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64())
assert.Equal(t, makeStackItem(reselements), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func makeProgram(opcodes ...Instruction) []byte {