neo-go/pkg/vm/vm_test.go
Roman Khimov a7457d08a1 vm/core: add ID support for SYSCALL, redo interop registration
This solves two problems:
 * adds support for shortened SYSCALL form that uses IDs (similar to #434, but
   for NEO 2.0, supporting both forms), which is important for compatibility
   with C# node and mainnet chain that uses it from some height
 * reworks interop plugging to use callbacks rather than appending to the map,
   these map mangling functions are clearly visible in the VM profiling
   statistics and we want spawning a VM to be fast, so it makes sense
   optimizing it. This change moves most of the work to the init() phase
   making VM setup cheaper.

Caveats:
 * InteropNameToID accepts `[]byte` because that's the thing we have in
   SYSCALL processing and that's the most often usecase for it, it leads to
   some conversions in other places but that's acceptable because those are
   either tests or init()
 * three getInterop functions are: `getDefaultVMInterop`, `getSystemInterop`
   and `getNeoInterop`

Our 100K (1.4M->1.5M) block import time improves by ~4% with this change.
2019-12-19 13:35:42 +03:00

2700 lines
68 KiB
Go

package vm
import (
"bytes"
"encoding/binary"
"encoding/hex"
"math/big"
"math/rand"
"testing"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func fooInteropGetter(id uint32) *InteropFuncPrice {
if id == InteropNameToID([]byte("foo")) {
return &InteropFuncPrice{func(evm *VM) error {
evm.Estack().PushVal(1)
return nil
}, 1}
}
return nil
}
func TestInteropHook(t *testing.T) {
v := New()
v.RegisterInteropGetter(fooInteropGetter)
buf := new(bytes.Buffer)
EmitSyscall(buf, "foo")
EmitOpcode(buf, opcode.RET)
v.Load(buf.Bytes())
runVM(t, v)
assert.Equal(t, 1, v.estack.Len())
assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value())
}
func TestInteropHookViaID(t *testing.T) {
v := New()
v.RegisterInteropGetter(fooInteropGetter)
buf := new(bytes.Buffer)
fooid := InteropNameToID([]byte("foo"))
var id = make([]byte, 4)
binary.LittleEndian.PutUint32(id, fooid)
_ = EmitSyscall(buf, string(id))
_ = EmitOpcode(buf, opcode.RET)
v.Load(buf.Bytes())
runVM(t, v)
assert.Equal(t, 1, v.estack.Len())
assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value())
}
func TestRegisterInteropGetter(t *testing.T) {
v := New()
currRegistered := len(v.getInterop)
v.RegisterInteropGetter(fooInteropGetter)
assert.Equal(t, currRegistered+1, len(v.getInterop))
}
func TestBytesToPublicKey(t *testing.T) {
v := New()
cache := v.GetPublicKeys()
assert.Equal(t, 0, len(cache))
keyHex := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
keyBytes, _ := hex.DecodeString(keyHex)
key := v.bytesToPublicKey(keyBytes)
assert.NotNil(t, key)
key2 := v.bytesToPublicKey(keyBytes)
assert.Equal(t, key, key2)
cache = v.GetPublicKeys()
assert.Equal(t, 1, len(cache))
assert.NotNil(t, cache[string(keyBytes)])
keyBytes[0] = 0xff
require.Panics(t, func() { v.bytesToPublicKey(keyBytes) })
}
func TestPushBytes1to75(t *testing.T) {
buf := new(bytes.Buffer)
for i := 1; i <= 75; i++ {
b := randomBytes(i)
EmitBytes(buf, b)
vm := load(buf.Bytes())
err := vm.Step()
require.NoError(t, err)
assert.Equal(t, 1, vm.estack.Len())
elem := vm.estack.Pop()
assert.IsType(t, &ByteArrayItem{}, elem.value)
assert.IsType(t, elem.Bytes(), b)
assert.Equal(t, 0, vm.estack.Len())
errExec := vm.execute(nil, opcode.RET, nil)
require.NoError(t, errExec)
assert.Equal(t, 0, vm.astack.Len())
assert.Equal(t, 0, vm.istack.Len())
buf.Reset()
}
}
func TestPushBytesNoParam(t *testing.T) {
prog := make([]byte, 1)
prog[0] = byte(opcode.PUSHBYTES1)
vm := load(prog)
checkVMFailed(t, vm)
}
func runVM(t *testing.T, vm *VM) {
err := vm.Run()
require.NoError(t, err)
assert.Equal(t, false, vm.HasFailed())
}
func checkVMFailed(t *testing.T, vm *VM) {
err := vm.Run()
require.Error(t, err)
assert.Equal(t, true, vm.HasFailed())
}
func TestStackLimitPUSH1Good(t *testing.T) {
prog := make([]byte, MaxStackSize*2)
for i := 0; i < MaxStackSize; i++ {
prog[i] = byte(opcode.PUSH1)
}
for i := MaxStackSize; i < MaxStackSize*2; i++ {
prog[i] = byte(opcode.DROP)
}
v := load(prog)
runVM(t, v)
}
func TestStackLimitPUSH1Bad(t *testing.T) {
prog := make([]byte, MaxStackSize+1)
for i := range prog {
prog[i] = byte(opcode.PUSH1)
}
v := load(prog)
checkVMFailed(t, v)
}
// appendBigStruct returns a program which:
// 1. pushes size Structs on stack
// 2. packs them into a new struct
// 3. appends them to a zero-length array
// Resulting stack size consists of:
// - struct (size+1)
// - array (1) of struct (size+1)
// which equals to size*2+3 elements in total.
func appendBigStruct(size uint16) []opcode.Opcode {
prog := make([]opcode.Opcode, size*2)
for i := uint16(0); i < size; i++ {
prog[i*2] = opcode.PUSH0
prog[i*2+1] = opcode.NEWSTRUCT
}
return append(prog,
opcode.PUSHBYTES2, opcode.Opcode(size), opcode.Opcode(size>>8), // LE
opcode.PACK, opcode.NEWSTRUCT,
opcode.DUP,
opcode.PUSH0, opcode.NEWARRAY, opcode.TOALTSTACK, opcode.DUPFROMALTSTACK,
opcode.SWAP,
opcode.APPEND, opcode.RET)
}
func TestStackLimitAPPENDStructGood(t *testing.T) {
prog := makeProgram(appendBigStruct(MaxStackSize/2 - 2)...)
v := load(prog)
runVM(t, v) // size = 2047 = (Max/2-2)*2+3 = Max-1
}
func TestStackLimitAPPENDStructBad(t *testing.T) {
prog := makeProgram(appendBigStruct(MaxStackSize/2 - 1)...)
v := load(prog)
checkVMFailed(t, v) // size = 2049 = (Max/2-1)*2+3 = Max+1
}
func TestStackLimit(t *testing.T) {
expected := []struct {
inst opcode.Opcode
size int
}{
{opcode.PUSH2, 1},
{opcode.NEWARRAY, 3}, // array + 2 items
{opcode.TOALTSTACK, 3},
{opcode.DUPFROMALTSTACK, 4},
{opcode.NEWSTRUCT, 6}, // all items are copied
{opcode.NEWMAP, 7},
{opcode.DUP, 8},
{opcode.PUSH2, 9},
{opcode.DUPFROMALTSTACK, 10},
{opcode.SETITEM, 8}, // -3 items and 1 new element in map
{opcode.DUP, 9},
{opcode.PUSH2, 10},
{opcode.DUPFROMALTSTACK, 11},
{opcode.SETITEM, 8}, // -3 items and no new elements in map
{opcode.DUP, 9},
{opcode.PUSH2, 10},
{opcode.REMOVE, 7}, // as we have right after NEWMAP
{opcode.DROP, 6}, // DROP map with no elements
}
prog := make([]opcode.Opcode, len(expected))
for i := range expected {
prog[i] = expected[i].inst
}
vm := load(makeProgram(prog...))
for i := range expected {
require.NoError(t, vm.Step())
require.Equal(t, expected[i].size, vm.size)
}
}
func TestPushBytesShort(t *testing.T) {
prog := make([]byte, 10)
prog[0] = byte(opcode.PUSHBYTES10) // but only 9 left in the `prog`
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushm1to16(t *testing.T) {
var prog []byte
for i := int(opcode.PUSHM1); i <= int(opcode.PUSH16); i++ {
if i == 80 {
continue // opcode layout we got here.
}
prog = append(prog, byte(i))
}
vm := load(prog)
for i := int(opcode.PUSHM1); i <= int(opcode.PUSH16); i++ {
if i == 80 {
continue // nice opcode layout we got here.
}
err := vm.Step()
require.NoError(t, err)
elem := vm.estack.Pop()
assert.IsType(t, &BigIntegerItem{}, elem.value)
val := i - int(opcode.PUSH1) + 1
assert.Equal(t, elem.BigInt().Int64(), int64(val))
}
}
func TestPushData1BadNoN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA1)}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData1BadN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA1), 1}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData1Good(t *testing.T) {
prog := makeProgram(opcode.PUSHDATA1, 3, 1, 2, 3)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
}
func TestPushData2BadNoN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA2)}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData2ShortN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA2), 0}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData2BadN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA2), 1, 0}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData2Good(t *testing.T) {
prog := makeProgram(opcode.PUSHDATA2, 3, 0, 1, 2, 3)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
}
func TestPushData4BadNoN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA4)}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData4BadN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA4), 1, 0, 0, 0}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData4ShortN(t *testing.T) {
prog := []byte{byte(opcode.PUSHDATA4), 0, 0, 0}
vm := load(prog)
checkVMFailed(t, vm)
}
func TestPushData4BigN(t *testing.T) {
prog := make([]byte, 1+4+MaxItemSize+1)
prog[0] = byte(opcode.PUSHDATA4)
binary.LittleEndian.PutUint32(prog[1:], MaxItemSize+1)
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.HasFailed())
}
func TestPushData4Good(t *testing.T) {
prog := makeProgram(opcode.PUSHDATA4, 3, 0, 0, 0, 1, 2, 3)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
}
func getSyscallProg(name string) (prog []byte) {
prog = []byte{byte(opcode.SYSCALL)}
prog = append(prog, byte(len(name)))
prog = append(prog, name...)
return
}
func getSerializeProg() (prog []byte) {
prog = append(prog, getSyscallProg("Neo.Runtime.Serialize")...)
prog = append(prog, getSyscallProg("Neo.Runtime.Deserialize")...)
prog = append(prog, byte(opcode.RET))
return
}
func testSerialize(t *testing.T, vm *VM) {
err := vm.Step()
require.NoError(t, err)
require.Equal(t, 1, vm.estack.Len())
require.IsType(t, (*ByteArrayItem)(nil), vm.estack.Top().value)
err = vm.Step()
require.NoError(t, err)
require.Equal(t, 1, vm.estack.Len())
}
func TestSerializeBool(t *testing.T) {
vm := load(getSerializeProg())
vm.estack.PushVal(true)
testSerialize(t, vm)
require.IsType(t, (*BoolItem)(nil), vm.estack.Top().value)
require.Equal(t, true, vm.estack.Top().Bool())
}
func TestSerializeByteArray(t *testing.T) {
vm := load(getSerializeProg())
value := []byte{1, 2, 3}
vm.estack.PushVal(value)
testSerialize(t, vm)
require.IsType(t, (*ByteArrayItem)(nil), vm.estack.Top().value)
require.Equal(t, value, vm.estack.Top().Bytes())
}
func TestSerializeInteger(t *testing.T) {
vm := load(getSerializeProg())
value := int64(123)
vm.estack.PushVal(value)
testSerialize(t, vm)
require.IsType(t, (*BigIntegerItem)(nil), vm.estack.Top().value)
require.Equal(t, value, vm.estack.Top().BigInt().Int64())
}
func TestSerializeArray(t *testing.T) {
vm := load(getSerializeProg())
item := NewArrayItem([]StackItem{
makeStackItem(true),
makeStackItem(123),
NewMapItem(),
})
vm.estack.Push(&Element{value: item})
testSerialize(t, vm)
require.IsType(t, (*ArrayItem)(nil), vm.estack.Top().value)
require.Equal(t, item.value, vm.estack.Top().Array())
}
func TestSerializeArrayBad(t *testing.T) {
vm := load(getSerializeProg())
item := NewArrayItem(makeArrayOfFalses(2))
item.value[1] = item
vm.estack.Push(&Element{value: item})
err := vm.Step()
require.Error(t, err)
require.True(t, vm.HasFailed())
}
func TestSerializeDupInteger(t *testing.T) {
prog := []byte{
byte(opcode.PUSH0), byte(opcode.NEWARRAY),
byte(opcode.DUP), byte(opcode.PUSH2), byte(opcode.DUP), byte(opcode.TOALTSTACK), byte(opcode.APPEND),
byte(opcode.DUP), byte(opcode.FROMALTSTACK), byte(opcode.APPEND),
}
vm := load(append(prog, getSerializeProg()...))
runVM(t, vm)
}
func TestSerializeStruct(t *testing.T) {
vm := load(getSerializeProg())
item := NewStructItem([]StackItem{
makeStackItem(true),
makeStackItem(123),
NewMapItem(),
})
vm.estack.Push(&Element{value: item})
testSerialize(t, vm)
require.IsType(t, (*StructItem)(nil), vm.estack.Top().value)
require.Equal(t, item.value, vm.estack.Top().Array())
}
func TestDeserializeUnknown(t *testing.T) {
prog := append(getSyscallProg("Neo.Runtime.Deserialize"), byte(opcode.RET))
vm := load(prog)
data, err := serializeItem(NewBigIntegerItem(123))
require.NoError(t, err)
data[0] = 0xFF
vm.estack.PushVal(data)
checkVMFailed(t, vm)
}
func TestSerializeMap(t *testing.T) {
vm := load(getSerializeProg())
item := NewMapItem()
item.Add(makeStackItem(true), makeStackItem([]byte{1, 2, 3}))
item.Add(makeStackItem([]byte{0}), makeStackItem(false))
vm.estack.Push(&Element{value: item})
testSerialize(t, vm)
require.IsType(t, (*MapItem)(nil), vm.estack.Top().value)
require.Equal(t, item.value, vm.estack.Top().value.(*MapItem).value)
}
func TestSerializeInterop(t *testing.T) {
vm := load(getSerializeProg())
item := NewInteropItem("kek")
vm.estack.Push(&Element{value: item})
err := vm.Step()
require.Error(t, err)
require.True(t, vm.HasFailed())
}
func callNTimes(n uint16) []byte {
return makeProgram(
opcode.PUSHBYTES2, opcode.Opcode(n), opcode.Opcode(n>>8), // little-endian
opcode.TOALTSTACK, opcode.DUPFROMALTSTACK,
opcode.JMPIF, 0x4, 0, opcode.RET,
opcode.FROMALTSTACK, opcode.DEC,
opcode.CALL, 0xF8, 0xFF) // -8 -> JMP to TOALTSTACK)
}
func TestInvocationLimitGood(t *testing.T) {
prog := callNTimes(MaxInvocationStackSize - 1)
v := load(prog)
runVM(t, v)
}
func TestInvocationLimitBad(t *testing.T) {
prog := callNTimes(MaxInvocationStackSize)
v := load(prog)
checkVMFailed(t, v)
}
func TestNOTNoArgument(t *testing.T) {
prog := makeProgram(opcode.NOT)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestNOTBool(t *testing.T) {
prog := makeProgram(opcode.NOT)
vm := load(prog)
vm.estack.PushVal(false)
runVM(t, vm)
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestNOTNonZeroInt(t *testing.T) {
prog := makeProgram(opcode.NOT)
vm := load(prog)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestNOTArray(t *testing.T) {
prog := makeProgram(opcode.NOT)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
runVM(t, vm)
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestNOTStruct(t *testing.T) {
prog := makeProgram(opcode.NOT)
vm := load(prog)
vm.estack.Push(NewElement(&StructItem{[]StackItem{}}))
runVM(t, vm)
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestNOTByteArray0(t *testing.T) {
prog := makeProgram(opcode.NOT)
vm := load(prog)
vm.estack.PushVal([]byte{0, 0})
runVM(t, vm)
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestNOTByteArray1(t *testing.T) {
prog := makeProgram(opcode.NOT)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
runVM(t, vm)
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
// getBigInt returns 2^a+b
func getBigInt(a, b int64) *big.Int {
p := new(big.Int).Exp(big.NewInt(2), big.NewInt(a), nil)
p.Add(p, big.NewInt(b))
return p
}
func TestAdd(t *testing.T) {
prog := makeProgram(opcode.ADD)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, int64(6), vm.estack.Pop().BigInt().Int64())
}
func TestADDBigResult(t *testing.T) {
prog := makeProgram(opcode.ADD)
vm := load(prog)
vm.estack.PushVal(getBigInt(MaxBigIntegerSizeBits, -1))
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func testBigArgument(t *testing.T, inst opcode.Opcode) {
prog := makeProgram(inst)
x := getBigInt(MaxBigIntegerSizeBits, 0)
t.Run(inst.String()+" big 1-st argument", func(t *testing.T) {
vm := load(prog)
vm.estack.PushVal(x)
vm.estack.PushVal(0)
checkVMFailed(t, vm)
})
t.Run(inst.String()+" big 2-nd argument", func(t *testing.T) {
vm := load(prog)
vm.estack.PushVal(0)
vm.estack.PushVal(x)
checkVMFailed(t, vm)
})
}
func TestArithBigArgument(t *testing.T) {
testBigArgument(t, opcode.ADD)
testBigArgument(t, opcode.SUB)
testBigArgument(t, opcode.MUL)
testBigArgument(t, opcode.DIV)
testBigArgument(t, opcode.MOD)
}
func TestMul(t *testing.T) {
prog := makeProgram(opcode.MUL)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, int64(8), vm.estack.Pop().BigInt().Int64())
}
func TestMULBigResult(t *testing.T) {
prog := makeProgram(opcode.MUL)
vm := load(prog)
vm.estack.PushVal(getBigInt(MaxBigIntegerSizeBits/2+1, 0))
vm.estack.PushVal(getBigInt(MaxBigIntegerSizeBits/2+1, 0))
checkVMFailed(t, vm)
}
func TestDiv(t *testing.T) {
prog := makeProgram(opcode.DIV)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64())
}
func TestSub(t *testing.T) {
prog := makeProgram(opcode.SUB)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64())
}
func TestSUBBigResult(t *testing.T) {
prog := makeProgram(opcode.SUB)
vm := load(prog)
vm.estack.PushVal(getBigInt(MaxBigIntegerSizeBits, -1))
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestSHRGood(t *testing.T) {
prog := makeProgram(opcode.SHR)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func TestSHRZero(t *testing.T) {
prog := makeProgram(opcode.SHR)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
vm.estack.PushVal(0)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
}
func TestSHRSmallValue(t *testing.T) {
prog := makeProgram(opcode.SHR)
vm := load(prog)
vm.estack.PushVal(5)
vm.estack.PushVal(minSHLArg - 1)
checkVMFailed(t, vm)
}
func TestSHRBigArgument(t *testing.T) {
prog := makeProgram(opcode.SHR)
vm := load(prog)
vm.estack.PushVal(getBigInt(MaxBigIntegerSizeBits, 0))
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestSHLGood(t *testing.T) {
prog := makeProgram(opcode.SHL)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(16), vm.estack.Pop().value)
}
func TestSHLZero(t *testing.T) {
prog := makeProgram(opcode.SHL)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
vm.estack.PushVal(0)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
}
func TestSHLBigValue(t *testing.T) {
prog := makeProgram(opcode.SHL)
vm := load(prog)
vm.estack.PushVal(5)
vm.estack.PushVal(maxSHLArg + 1)
checkVMFailed(t, vm)
}
func TestSHLBigResult(t *testing.T) {
prog := makeProgram(opcode.SHL)
vm := load(prog)
vm.estack.PushVal(getBigInt(MaxBigIntegerSizeBits/2, 0))
vm.estack.PushVal(MaxBigIntegerSizeBits / 2)
checkVMFailed(t, vm)
}
func TestSHLBigArgument(t *testing.T) {
prog := makeProgram(opcode.SHR)
vm := load(prog)
vm.estack.PushVal(getBigInt(MaxBigIntegerSizeBits, 0))
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestLT(t *testing.T) {
prog := makeProgram(opcode.LT)
vm := load(prog)
vm.estack.PushVal(4)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestLTE(t *testing.T) {
prog := makeProgram(opcode.LTE)
vm := load(prog)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestGT(t *testing.T) {
prog := makeProgram(opcode.GT)
vm := load(prog)
vm.estack.PushVal(9)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestGTE(t *testing.T) {
prog := makeProgram(opcode.GTE)
vm := load(prog)
vm.estack.PushVal(3)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestDepth(t *testing.T) {
prog := makeProgram(opcode.DEPTH)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64())
}
func TestEQUALNoArguments(t *testing.T) {
prog := makeProgram(opcode.EQUAL)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestEQUALBad1Argument(t *testing.T) {
prog := makeProgram(opcode.EQUAL)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestEQUALGoodInteger(t *testing.T) {
prog := makeProgram(opcode.EQUAL)
vm := load(prog)
vm.estack.PushVal(5)
vm.estack.PushVal(5)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestEQUALArrayTrue(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.EQUAL)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestEQUALArrayFalse(t *testing.T) {
prog := makeProgram(opcode.EQUAL)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
vm.estack.PushVal([]StackItem{})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestEQUALMapTrue(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.EQUAL)
vm := load(prog)
vm.estack.Push(&Element{value: NewMapItem()})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value)
}
func TestEQUALMapFalse(t *testing.T) {
prog := makeProgram(opcode.EQUAL)
vm := load(prog)
vm.estack.Push(&Element{value: NewMapItem()})
vm.estack.Push(&Element{value: NewMapItem()})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BoolItem{false}, vm.estack.Pop().value)
}
func TestNumEqual(t *testing.T) {
prog := makeProgram(opcode.NUMEQUAL)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestNumNotEqual(t *testing.T) {
prog := makeProgram(opcode.NUMNOTEQUAL)
vm := load(prog)
vm.estack.PushVal(2)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestINC(t *testing.T) {
prog := makeProgram(opcode.INC)
vm := load(prog)
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, big.NewInt(2), vm.estack.Pop().BigInt())
}
func TestINCBigResult(t *testing.T) {
prog := makeProgram(opcode.INC, opcode.INC)
vm := load(prog)
x := getBigInt(MaxBigIntegerSizeBits, -2)
vm.estack.PushVal(x)
require.NoError(t, vm.Step())
require.False(t, vm.HasFailed())
require.Equal(t, 1, vm.estack.Len())
require.Equal(t, new(big.Int).Add(x, big.NewInt(1)), vm.estack.Top().BigInt())
checkVMFailed(t, vm)
}
func TestDECBigResult(t *testing.T) {
prog := makeProgram(opcode.DEC, opcode.DEC)
vm := load(prog)
x := getBigInt(MaxBigIntegerSizeBits, -2)
x.Neg(x)
vm.estack.PushVal(x)
require.NoError(t, vm.Step())
require.False(t, vm.HasFailed())
require.Equal(t, 1, vm.estack.Len())
require.Equal(t, new(big.Int).Sub(x, big.NewInt(1)), vm.estack.Top().BigInt())
checkVMFailed(t, vm)
}
func TestNEWARRAYInteger(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY)
vm := load(prog)
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{[]StackItem{makeStackItem(false)}}, vm.estack.Pop().value)
}
func TestNEWARRAYStruct(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY)
vm := load(prog)
arr := []StackItem{makeStackItem(42)}
vm.estack.Push(&Element{value: &StructItem{arr}})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value)
}
func testNEWARRAYIssue437(t *testing.T, i1, i2 opcode.Opcode, appended bool) {
prog := makeProgram(
opcode.PUSH2, i1,
opcode.DUP, opcode.PUSH3, opcode.APPEND,
opcode.TOALTSTACK, opcode.DUPFROMALTSTACK, i2,
opcode.DUP, opcode.PUSH4, opcode.APPEND,
opcode.FROMALTSTACK, opcode.PUSH5, opcode.APPEND)
vm := load(prog)
vm.Run()
arr := makeArrayOfFalses(4)
arr[2] = makeStackItem(3)
arr[3] = makeStackItem(4)
if appended {
arr = append(arr, makeStackItem(5))
}
assert.Equal(t, false, vm.HasFailed())
assert.Equal(t, 1, vm.estack.Len())
if i2 == opcode.NEWARRAY {
assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value)
} else {
assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value)
}
}
func TestNEWARRAYIssue437(t *testing.T) {
t.Run("Array+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) })
t.Run("Struct+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) })
t.Run("Array+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) })
t.Run("Struct+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) })
}
func TestNEWARRAYArray(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY)
vm := load(prog)
arr := []StackItem{makeStackItem(42)}
vm.estack.Push(&Element{value: &ArrayItem{arr}})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value)
}
func TestNEWARRAYByteArray(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY)
vm := load(prog)
vm.estack.PushVal([]byte{})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{[]StackItem{}}, vm.estack.Pop().value)
}
func TestNEWARRAYBadSize(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY)
vm := load(prog)
vm.estack.PushVal(MaxArraySize + 1)
checkVMFailed(t, vm)
}
func TestNEWSTRUCTInteger(t *testing.T) {
prog := makeProgram(opcode.NEWSTRUCT)
vm := load(prog)
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &StructItem{[]StackItem{makeStackItem(false)}}, vm.estack.Pop().value)
}
func TestNEWSTRUCTArray(t *testing.T) {
prog := makeProgram(opcode.NEWSTRUCT)
vm := load(prog)
arr := []StackItem{makeStackItem(42)}
vm.estack.Push(&Element{value: &ArrayItem{arr}})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value)
}
func TestNEWSTRUCTStruct(t *testing.T) {
prog := makeProgram(opcode.NEWSTRUCT)
vm := load(prog)
arr := []StackItem{makeStackItem(42)}
vm.estack.Push(&Element{value: &StructItem{arr}})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value)
}
func TestNEWSTRUCTByteArray(t *testing.T) {
prog := makeProgram(opcode.NEWSTRUCT)
vm := load(prog)
vm.estack.PushVal([]byte{})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &StructItem{[]StackItem{}}, vm.estack.Pop().value)
}
func TestNEWSTRUCTBadSize(t *testing.T) {
prog := makeProgram(opcode.NEWSTRUCT)
vm := load(prog)
vm.estack.PushVal(MaxArraySize + 1)
checkVMFailed(t, vm)
}
func TestAPPENDArray(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.PUSH5, opcode.APPEND)
vm := load(prog)
vm.estack.Push(&Element{value: &ArrayItem{}})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{[]StackItem{makeStackItem(5)}}, vm.estack.Pop().value)
}
func TestAPPENDStruct(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.PUSH5, opcode.APPEND)
vm := load(prog)
vm.estack.Push(&Element{value: &StructItem{}})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &StructItem{[]StackItem{makeStackItem(5)}}, vm.estack.Pop().value)
}
func TestAPPENDCloneStruct(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.PUSH0, opcode.NEWSTRUCT, opcode.TOALTSTACK,
opcode.DUPFROMALTSTACK, opcode.APPEND, opcode.FROMALTSTACK, opcode.PUSH1, opcode.APPEND)
vm := load(prog)
vm.estack.Push(&Element{value: &ArrayItem{}})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{[]StackItem{
&StructItem{[]StackItem{}},
}}, vm.estack.Pop().value)
}
func TestAPPENDBadNoArguments(t *testing.T) {
prog := makeProgram(opcode.APPEND)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestAPPENDBad1Argument(t *testing.T) {
prog := makeProgram(opcode.APPEND)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestAPPENDWrongType(t *testing.T) {
prog := makeProgram(opcode.APPEND)
vm := load(prog)
vm.estack.PushVal([]byte{})
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestAPPENDGoodSizeLimit(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY, opcode.DUP, opcode.PUSH0, opcode.APPEND)
vm := load(prog)
vm.estack.PushVal(MaxArraySize - 1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, MaxArraySize, len(vm.estack.Pop().Array()))
}
func TestAPPENDBadSizeLimit(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY, opcode.DUP, opcode.PUSH0, opcode.APPEND)
vm := load(prog)
vm.estack.PushVal(MaxArraySize)
checkVMFailed(t, vm)
}
func TestPICKITEMBadIndex(t *testing.T) {
prog := makeProgram(opcode.PICKITEM)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
vm.estack.PushVal(0)
checkVMFailed(t, vm)
}
func TestPICKITEMArray(t *testing.T) {
prog := makeProgram(opcode.PICKITEM)
vm := load(prog)
vm.estack.PushVal([]StackItem{makeStackItem(1), makeStackItem(2)})
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestPICKITEMByteArray(t *testing.T) {
prog := makeProgram(opcode.PICKITEM)
vm := load(prog)
vm.estack.PushVal([]byte{1, 2})
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestPICKITEMDupArray(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.PUSH0, opcode.PICKITEM, opcode.ABS)
vm := load(prog)
vm.estack.PushVal([]StackItem{makeStackItem(-1)})
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
items := vm.estack.Pop().Value().([]StackItem)
assert.Equal(t, big.NewInt(-1), items[0].Value())
}
func TestPICKITEMDupMap(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.PUSHBYTES1, 42, opcode.PICKITEM, opcode.ABS)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem([]byte{42}), makeStackItem(-1))
vm.estack.Push(&Element{value: m})
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
items := vm.estack.Pop().Value().(map[interface{}]StackItem)
assert.Equal(t, big.NewInt(-1), items[string([]byte{42})].Value())
}
func TestPICKITEMMap(t *testing.T) {
prog := makeProgram(opcode.PICKITEM)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem(3))
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(makeStackItem(5))
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(3), vm.estack.Pop().value)
}
func TestSETITEMMap(t *testing.T) {
prog := makeProgram(opcode.SETITEM, opcode.PICKITEM)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem(3))
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(5)
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(5)
vm.estack.PushVal([]byte{0, 1})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
}
func TestSETITEMBigMapBad(t *testing.T) {
prog := makeProgram(opcode.SETITEM)
vm := load(prog)
m := NewMapItem()
for i := 0; i < MaxArraySize; i++ {
m.Add(makeStackItem(i), makeStackItem(i))
}
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(MaxArraySize)
vm.estack.PushVal(0)
checkVMFailed(t, vm)
}
func TestSETITEMBigMapGood(t *testing.T) {
prog := makeProgram(opcode.SETITEM)
vm := load(prog)
m := NewMapItem()
for i := 0; i < MaxArraySize; i++ {
m.Add(makeStackItem(i), makeStackItem(i))
}
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(0)
vm.estack.PushVal(0)
runVM(t, vm)
}
func TestSIZENoArgument(t *testing.T) {
prog := makeProgram(opcode.SIZE)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestSIZEByteArray(t *testing.T) {
prog := makeProgram(opcode.SIZE)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestSIZEBool(t *testing.T) {
prog := makeProgram(opcode.SIZE)
vm := load(prog)
vm.estack.PushVal(false)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
// assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
// FIXME revert when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
assert.Equal(t, makeStackItem(0), vm.estack.Pop().value)
}
func TestARRAYSIZEArray(t *testing.T) {
prog := makeProgram(opcode.ARRAYSIZE)
vm := load(prog)
vm.estack.PushVal([]StackItem{
makeStackItem(1),
makeStackItem([]byte{}),
})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestARRAYSIZEMap(t *testing.T) {
prog := makeProgram(opcode.ARRAYSIZE)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem(6))
m.Add(makeStackItem([]byte{0, 1}), makeStackItem(6))
vm.estack.Push(&Element{value: m})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestKEYSMap(t *testing.T) {
prog := makeProgram(opcode.KEYS)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem(6))
m.Add(makeStackItem([]byte{0, 1}), makeStackItem(6))
vm.estack.Push(&Element{value: m})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
top := vm.estack.Pop().value.(*ArrayItem)
assert.Equal(t, 2, len(top.value))
assert.Contains(t, top.value, makeStackItem(5))
assert.Contains(t, top.value, makeStackItem([]byte{0, 1}))
}
func TestKEYSNoArgument(t *testing.T) {
prog := makeProgram(opcode.KEYS)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestKEYSWrongType(t *testing.T) {
prog := makeProgram(opcode.KEYS)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
checkVMFailed(t, vm)
}
func TestVALUESMap(t *testing.T) {
prog := makeProgram(opcode.VALUES)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem([]byte{2, 3}))
m.Add(makeStackItem([]byte{0, 1}), makeStackItem([]StackItem{}))
vm.estack.Push(&Element{value: m})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
top := vm.estack.Pop().value.(*ArrayItem)
assert.Equal(t, 2, len(top.value))
assert.Contains(t, top.value, makeStackItem([]byte{2, 3}))
assert.Contains(t, top.value, makeStackItem([]StackItem{}))
}
func TestVALUESArray(t *testing.T) {
prog := makeProgram(opcode.VALUES)
vm := load(prog)
vm.estack.PushVal([]StackItem{makeStackItem(4)})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ArrayItem{[]StackItem{makeStackItem(4)}}, vm.estack.Pop().value)
}
func TestVALUESNoArgument(t *testing.T) {
prog := makeProgram(opcode.VALUES)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestVALUESWrongType(t *testing.T) {
prog := makeProgram(opcode.VALUES)
vm := load(prog)
vm.estack.PushVal(5)
checkVMFailed(t, vm)
}
func TestHASKEYArrayTrue(t *testing.T) {
prog := makeProgram(opcode.PUSH5, opcode.NEWARRAY, opcode.PUSH4, opcode.HASKEY)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(true), vm.estack.Pop().value)
}
func TestHASKEYArrayFalse(t *testing.T) {
prog := makeProgram(opcode.PUSH5, opcode.NEWARRAY, opcode.PUSH5, opcode.HASKEY)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(false), vm.estack.Pop().value)
}
func TestHASKEYStructTrue(t *testing.T) {
prog := makeProgram(opcode.PUSH5, opcode.NEWSTRUCT, opcode.PUSH4, opcode.HASKEY)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(true), vm.estack.Pop().value)
}
func TestHASKEYStructFalse(t *testing.T) {
prog := makeProgram(opcode.PUSH5, opcode.NEWSTRUCT, opcode.PUSH5, opcode.HASKEY)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(false), vm.estack.Pop().value)
}
func TestHASKEYMapTrue(t *testing.T) {
prog := makeProgram(opcode.HASKEY)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem(6))
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(5)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(true), vm.estack.Pop().value)
}
func TestHASKEYMapFalse(t *testing.T) {
prog := makeProgram(opcode.HASKEY)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem(6))
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(6)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(false), vm.estack.Pop().value)
}
func TestHASKEYNoArguments(t *testing.T) {
prog := makeProgram(opcode.HASKEY)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestHASKEY1Argument(t *testing.T) {
prog := makeProgram(opcode.HASKEY)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestHASKEYWrongKeyType(t *testing.T) {
prog := makeProgram(opcode.HASKEY)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
vm.estack.PushVal([]StackItem{})
checkVMFailed(t, vm)
}
func TestHASKEYWrongCollectionType(t *testing.T) {
prog := makeProgram(opcode.HASKEY)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
checkVMFailed(t, vm)
}
func TestSIGNNoArgument(t *testing.T) {
prog := makeProgram(opcode.SIGN)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestSIGNWrongType(t *testing.T) {
prog := makeProgram(opcode.SIGN)
vm := load(prog)
vm.estack.PushVal([]StackItem{})
checkVMFailed(t, vm)
}
func TestSIGNBool(t *testing.T) {
prog := makeProgram(opcode.SIGN)
vm := load(prog)
vm.estack.PushVal(false)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(0)}, vm.estack.Pop().value)
}
func TestSIGNPositiveInt(t *testing.T) {
prog := makeProgram(opcode.SIGN)
vm := load(prog)
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(1)}, vm.estack.Pop().value)
}
func TestSIGNNegativeInt(t *testing.T) {
prog := makeProgram(opcode.SIGN)
vm := load(prog)
vm.estack.PushVal(-1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(-1)}, vm.estack.Pop().value)
}
func TestSIGNZero(t *testing.T) {
prog := makeProgram(opcode.SIGN)
vm := load(prog)
vm.estack.PushVal(0)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(0)}, vm.estack.Pop().value)
}
func TestSIGNByteArray(t *testing.T) {
prog := makeProgram(opcode.SIGN)
vm := load(prog)
vm.estack.PushVal([]byte{0, 1})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &BigIntegerItem{big.NewInt(1)}, vm.estack.Pop().value)
}
func TestAppCall(t *testing.T) {
prog := []byte{byte(opcode.APPCALL)}
hash := util.Uint160{}
prog = append(prog, hash.BytesBE()...)
prog = append(prog, byte(opcode.RET))
vm := load(prog)
vm.SetScriptGetter(func(in util.Uint160) []byte {
if in.Equals(hash) {
return makeProgram(opcode.DEPTH)
}
return nil
})
vm.estack.PushVal(2)
runVM(t, vm)
elem := vm.estack.Pop() // depth should be 1
assert.Equal(t, int64(1), elem.BigInt().Int64())
}
func TestSimpleCall(t *testing.T) {
progStr := "52c56b525a7c616516006c766b00527ac46203006c766b00c3616c756653c56b6c766b00527ac46c766b51527ac46203006c766b00c36c766b51c393616c7566"
result := 12
prog, err := hex.DecodeString(progStr)
if err != nil {
t.Fatal(err)
}
vm := load(prog)
runVM(t, vm)
assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64()))
}
func TestNZtrue(t *testing.T) {
prog := makeProgram(opcode.NZ)
vm := load(prog)
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestNZfalse(t *testing.T) {
prog := makeProgram(opcode.NZ)
vm := load(prog)
vm.estack.PushVal(0)
runVM(t, vm)
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestPICKbadNoitem(t *testing.T) {
prog := makeProgram(opcode.PICK)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestPICKbadNegative(t *testing.T) {
prog := makeProgram(opcode.PICK)
vm := load(prog)
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestPICKgood(t *testing.T) {
prog := makeProgram(opcode.PICK)
result := 2
vm := load(prog)
vm.estack.PushVal(0)
vm.estack.PushVal(1)
vm.estack.PushVal(result)
vm.estack.PushVal(3)
vm.estack.PushVal(4)
vm.estack.PushVal(5)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64())
}
func TestPICKDup(t *testing.T) {
prog := makeProgram(opcode.PUSHM1, opcode.PUSH0,
opcode.PUSH1,
opcode.PUSH2,
opcode.PICK,
opcode.ABS)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 4, vm.estack.Len())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(0), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(-1), vm.estack.Pop().BigInt().Int64())
}
func TestROTBad(t *testing.T) {
prog := makeProgram(opcode.ROT)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
checkVMFailed(t, vm)
}
func TestROTGood(t *testing.T) {
prog := makeProgram(opcode.ROT)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
runVM(t, vm)
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 TestROLLBad1(t *testing.T) {
prog := makeProgram(opcode.ROLL)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestROLLBad2(t *testing.T) {
prog := makeProgram(opcode.ROLL)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
vm.estack.PushVal(3)
checkVMFailed(t, vm)
}
func TestROLLGood(t *testing.T) {
prog := makeProgram(opcode.ROLL)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
vm.estack.PushVal(4)
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 4, vm.estack.Len())
assert.Equal(t, makeStackItem(3), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(4), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func TestXTUCKbadNoitem(t *testing.T) {
prog := makeProgram(opcode.XTUCK)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestXTUCKbadNoN(t *testing.T) {
prog := makeProgram(opcode.XTUCK)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
checkVMFailed(t, vm)
}
func TestXTUCKbadNegative(t *testing.T) {
prog := makeProgram(opcode.XTUCK)
vm := load(prog)
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestXTUCKbadZero(t *testing.T) {
prog := makeProgram(opcode.XTUCK)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(0)
checkVMFailed(t, vm)
}
func TestXTUCKgood(t *testing.T) {
prog := makeProgram(opcode.XTUCK)
topelement := 5
xtuckdepth := 3
vm := load(prog)
vm.estack.PushVal(0)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
vm.estack.PushVal(4)
vm.estack.PushVal(topelement)
vm.estack.PushVal(xtuckdepth)
runVM(t, vm)
assert.Equal(t, int64(topelement), vm.estack.Peek(0).BigInt().Int64())
assert.Equal(t, int64(topelement), vm.estack.Peek(xtuckdepth).BigInt().Int64())
}
func TestTUCKbadNoitems(t *testing.T) {
prog := makeProgram(opcode.TUCK)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestTUCKbadNoitem(t *testing.T) {
prog := makeProgram(opcode.TUCK)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestTUCKgood(t *testing.T) {
prog := makeProgram(opcode.TUCK)
vm := load(prog)
vm.estack.PushVal(42)
vm.estack.PushVal(34)
runVM(t, vm)
assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64())
assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64())
assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64())
}
func TestTUCKgood2(t *testing.T) {
prog := makeProgram(opcode.TUCK)
vm := load(prog)
vm.estack.PushVal(11)
vm.estack.PushVal(42)
vm.estack.PushVal(34)
runVM(t, vm)
assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64())
assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64())
assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64())
assert.Equal(t, int64(11), vm.estack.Peek(3).BigInt().Int64())
}
func TestOVERbadNoitem(t *testing.T) {
prog := makeProgram(opcode.OVER)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func TestOVERbadNoitems(t *testing.T) {
prog := makeProgram(opcode.OVER)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestOVERgood(t *testing.T) {
prog := makeProgram(opcode.OVER)
vm := load(prog)
vm.estack.PushVal(42)
vm.estack.PushVal(34)
runVM(t, vm)
assert.Equal(t, int64(42), vm.estack.Peek(0).BigInt().Int64())
assert.Equal(t, int64(34), vm.estack.Peek(1).BigInt().Int64())
assert.Equal(t, int64(42), vm.estack.Peek(2).BigInt().Int64())
assert.Equal(t, 3, vm.estack.Len())
}
func TestOVERDup(t *testing.T) {
prog := makeProgram(opcode.PUSHBYTES2, 1, 0,
opcode.PUSH1,
opcode.OVER,
opcode.PUSH1,
opcode.LEFT,
opcode.PUSHBYTES1, 2,
opcode.CAT)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 3, vm.estack.Len())
assert.Equal(t, []byte{0x01, 0x02}, vm.estack.Pop().Bytes())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, []byte{0x01, 0x00}, vm.estack.Pop().Bytes())
}
func TestNIPBadNoItem(t *testing.T) {
prog := makeProgram(opcode.NIP)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestNIPGood(t *testing.T) {
prog := makeProgram(opcode.NIP)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
}
func TestDROPBadNoItem(t *testing.T) {
prog := makeProgram(opcode.DROP)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestDROPGood(t *testing.T) {
prog := makeProgram(opcode.DROP)
vm := load(prog)
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 0, vm.estack.Len())
}
func TestXDROPbadNoitem(t *testing.T) {
prog := makeProgram(opcode.XDROP)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestXDROPbadNoN(t *testing.T) {
prog := makeProgram(opcode.XDROP)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
checkVMFailed(t, vm)
}
func TestXDROPbadNegative(t *testing.T) {
prog := makeProgram(opcode.XDROP)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestXDROPgood(t *testing.T) {
prog := makeProgram(opcode.XDROP)
vm := load(prog)
vm.estack.PushVal(0)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, int64(2), vm.estack.Peek(0).BigInt().Int64())
assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64())
}
func TestINVERTbadNoitem(t *testing.T) {
prog := makeProgram(opcode.INVERT)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestINVERTgood1(t *testing.T) {
prog := makeProgram(opcode.INVERT)
vm := load(prog)
vm.estack.PushVal(0)
runVM(t, vm)
assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64())
}
func TestINVERTgood2(t *testing.T) {
prog := makeProgram(opcode.INVERT)
vm := load(prog)
vm.estack.PushVal(-1)
runVM(t, vm)
assert.Equal(t, int64(0), vm.estack.Peek(0).BigInt().Int64())
}
func TestINVERTgood3(t *testing.T) {
prog := makeProgram(opcode.INVERT)
vm := load(prog)
vm.estack.PushVal(0x69)
runVM(t, vm)
assert.Equal(t, int64(-0x6A), vm.estack.Peek(0).BigInt().Int64())
}
func TestINVERTWithConversion1(t *testing.T) {
prog := makeProgram(opcode.PUSHDATA2, 0, 0, opcode.INVERT)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64())
}
func TestINVERTWithConversion2(t *testing.T) {
prog := makeProgram(opcode.PUSH0, opcode.PUSH1, opcode.NUMEQUAL, opcode.INVERT)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64())
}
func TestCATBadNoArgs(t *testing.T) {
prog := makeProgram(opcode.CAT)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestCATBadOneArg(t *testing.T) {
prog := makeProgram(opcode.CAT)
vm := load(prog)
vm.estack.PushVal([]byte("abc"))
checkVMFailed(t, vm)
}
func TestCATBadBigItem(t *testing.T) {
prog := makeProgram(opcode.CAT)
vm := load(prog)
vm.estack.PushVal(make([]byte, MaxItemSize/2+1))
vm.estack.PushVal(make([]byte, MaxItemSize/2+1))
vm.Run()
assert.Equal(t, true, vm.HasFailed())
}
func TestCATGood(t *testing.T) {
prog := makeProgram(opcode.CAT)
vm := load(prog)
vm.estack.PushVal([]byte("abc"))
vm.estack.PushVal([]byte("def"))
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("abcdef"), vm.estack.Peek(0).Bytes())
}
func TestCATInt0ByteArray(t *testing.T) {
prog := makeProgram(opcode.CAT)
vm := load(prog)
vm.estack.PushVal(0)
vm.estack.PushVal([]byte{})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ByteArrayItem{[]byte{}}, vm.estack.Pop().value)
}
func TestCATByteArrayInt1(t *testing.T) {
prog := makeProgram(opcode.CAT)
vm := load(prog)
vm.estack.PushVal([]byte{})
vm.estack.PushVal(1)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ByteArrayItem{[]byte{1}}, vm.estack.Pop().value)
}
func TestSUBSTRBadNoArgs(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestSUBSTRBadOneArg(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestSUBSTRBadTwoArgs(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
vm.estack.PushVal(0)
vm.estack.PushVal(2)
checkVMFailed(t, vm)
}
func TestSUBSTRGood(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(1)
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("bc"), vm.estack.Peek(0).Bytes())
}
func TestSUBSTRBadOffset(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(7)
vm.estack.PushVal(1)
// checkVMFailed(t, vm)
// FIXME revert when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{}, vm.estack.Peek(0).Bytes())
}
func TestSUBSTRBigLen(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(1)
vm.estack.PushVal(6)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes())
}
func TestSUBSTRBad387(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
b := make([]byte, 6, 20)
copy(b, "abcdef")
vm.estack.PushVal(b)
vm.estack.PushVal(1)
vm.estack.PushVal(6)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes())
}
func TestSUBSTRBadNegativeOffset(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(-1)
vm.estack.PushVal(3)
checkVMFailed(t, vm)
}
func TestSUBSTRBadNegativeLen(t *testing.T) {
prog := makeProgram(opcode.SUBSTR)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(3)
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestLEFTBadNoArgs(t *testing.T) {
prog := makeProgram(opcode.LEFT)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestLEFTBadNoString(t *testing.T) {
prog := makeProgram(opcode.LEFT)
vm := load(prog)
vm.estack.PushVal(2)
checkVMFailed(t, vm)
}
func TestLEFTBadNegativeLen(t *testing.T) {
prog := makeProgram(opcode.LEFT)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestLEFTGood(t *testing.T) {
prog := makeProgram(opcode.LEFT)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("ab"), vm.estack.Peek(0).Bytes())
}
func TestLEFTGoodLen(t *testing.T) {
prog := makeProgram(opcode.LEFT)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(8)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("abcdef"), vm.estack.Peek(0).Bytes())
}
func TestRIGHTBadNoArgs(t *testing.T) {
prog := makeProgram(opcode.RIGHT)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestRIGHTBadNoString(t *testing.T) {
prog := makeProgram(opcode.RIGHT)
vm := load(prog)
vm.estack.PushVal(2)
checkVMFailed(t, vm)
}
func TestRIGHTBadNegativeLen(t *testing.T) {
prog := makeProgram(opcode.RIGHT)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestRIGHTGood(t *testing.T) {
prog := makeProgram(opcode.RIGHT)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(2)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("ef"), vm.estack.Peek(0).Bytes())
}
func TestRIGHTBadLen(t *testing.T) {
prog := makeProgram(opcode.RIGHT)
vm := load(prog)
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(8)
checkVMFailed(t, vm)
}
func TestPACKBadLen(t *testing.T) {
prog := makeProgram(opcode.PACK)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestPACKBigLen(t *testing.T) {
prog := makeProgram(opcode.PACK)
vm := load(prog)
for i := 0; i <= MaxArraySize; i++ {
vm.estack.PushVal(0)
}
vm.estack.PushVal(MaxArraySize + 1)
checkVMFailed(t, vm)
}
func TestPACKGoodZeroLen(t *testing.T) {
prog := makeProgram(opcode.PACK)
vm := load(prog)
vm.estack.PushVal(0)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []StackItem{}, vm.estack.Peek(0).Array())
}
func TestPACKGood(t *testing.T) {
prog := makeProgram(opcode.PACK)
elements := []int{55, 34, 42}
vm := load(prog)
// canary
vm.estack.PushVal(1)
for i := len(elements) - 1; i >= 0; i-- {
vm.estack.PushVal(elements[i])
}
vm.estack.PushVal(len(elements))
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
a := vm.estack.Peek(0).Array()
assert.Equal(t, len(elements), len(a))
for i := 0; i < len(elements); i++ {
e := a[i].Value().(*big.Int)
assert.Equal(t, int64(elements[i]), e.Int64())
}
assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64())
}
func TestUNPACKBadNotArray(t *testing.T) {
prog := makeProgram(opcode.UNPACK)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestUNPACKGood(t *testing.T) {
prog := makeProgram(opcode.UNPACK)
elements := []int{55, 34, 42}
vm := load(prog)
// canary
vm.estack.PushVal(1)
vm.estack.PushVal(elements)
runVM(t, vm)
assert.Equal(t, 5, vm.estack.Len())
assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).BigInt().Int64())
for k, v := range elements {
assert.Equal(t, int64(v), vm.estack.Peek(k+1).BigInt().Int64())
}
assert.Equal(t, int64(1), vm.estack.Peek(len(elements)+1).BigInt().Int64())
}
func TestREVERSEBadNotArray(t *testing.T) {
prog := makeProgram(opcode.REVERSE)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func testREVERSEIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) {
prog := makeProgram(
opcode.PUSH0, i1,
opcode.DUP, opcode.PUSH1, opcode.APPEND,
opcode.DUP, opcode.PUSH2, opcode.APPEND,
opcode.DUP, i2, opcode.REVERSE)
vm := load(prog)
vm.Run()
arr := make([]StackItem, 2)
if reversed {
arr[0] = makeStackItem(2)
arr[1] = makeStackItem(1)
} else {
arr[0] = makeStackItem(1)
arr[1] = makeStackItem(2)
}
assert.Equal(t, false, vm.HasFailed())
assert.Equal(t, 1, vm.estack.Len())
if i1 == opcode.NEWARRAY {
assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value)
} else {
assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value)
}
}
func TestREVERSEIssue437(t *testing.T) {
t.Run("Array+Array", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) })
t.Run("Struct+Struct", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) })
t.Run("Array+Struct", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) })
t.Run("Struct+Array", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) })
}
func TestREVERSEGoodOneElem(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.REVERSE)
elements := []int{22}
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(elements)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
a := vm.estack.Peek(0).Array()
assert.Equal(t, len(elements), len(a))
e := a[0].Value().(*big.Int)
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(opcode.DUP, opcode.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}})
runVM(t, vm)
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(opcode.DUP, opcode.REVERSE)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(elements)
runVM(t, vm)
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 TestREMOVEBadNoArgs(t *testing.T) {
prog := makeProgram(opcode.REMOVE)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestREMOVEBadOneArg(t *testing.T) {
prog := makeProgram(opcode.REMOVE)
vm := load(prog)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestREMOVEBadNotArray(t *testing.T) {
prog := makeProgram(opcode.REMOVE)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(1)
checkVMFailed(t, vm)
}
func TestREMOVEBadIndex(t *testing.T) {
prog := makeProgram(opcode.REMOVE)
elements := []int{22, 34, 42, 55, 81}
vm := load(prog)
vm.estack.PushVal(elements)
vm.estack.PushVal(10)
checkVMFailed(t, vm)
}
func TestREMOVEGood(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.PUSH2, opcode.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)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, makeStackItem(reselements), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func TestREMOVEMap(t *testing.T) {
prog := makeProgram(opcode.REMOVE, opcode.PUSH5, opcode.HASKEY)
vm := load(prog)
m := NewMapItem()
m.Add(makeStackItem(5), makeStackItem(3))
m.Add(makeStackItem([]byte{0, 1}), makeStackItem([]byte{2, 3}))
vm.estack.Push(&Element{value: m})
vm.estack.Push(&Element{value: m})
vm.estack.PushVal(makeStackItem(5))
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, makeStackItem(false), vm.estack.Pop().value)
}
func TestCHECKSIGNoArgs(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestCHECKSIGOneArg(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(pbytes)
checkVMFailed(t, vm)
}
func TestCHECKSIGNoSigLoaded(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := "NEO - An Open Network For Smart Economy"
sig, err := pk.Sign([]byte(msg))
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
checkVMFailed(t, vm)
}
func TestCHECKSIGBadKey(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig, err := pk.Sign(msg)
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()[:4]
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
checkVMFailed(t, vm)
}
func TestCHECKSIGWrongSig(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig, err := pk.Sign(msg)
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal(util.ArrayReverse(sig))
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestCHECKSIGGood(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig, err := pk.Sign(msg)
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestVERIFYGood(t *testing.T) {
prog := makeProgram(opcode.VERIFY)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig, err := pk.Sign(msg)
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(msg)
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestVERIFYBad(t *testing.T) {
prog := makeProgram(opcode.VERIFY)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig, err := pk.Sign(msg)
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(util.ArrayReverse(msg))
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestCHECKMULTISIGNoArgs(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGOneArg(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
vm := load(prog)
pbytes := pk.PublicKey().Bytes()
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGNotEnoughKeys(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1, err := pk1.Sign(msg)
assert.Nil(t, err)
sig2, err := pk2.Sign(msg)
assert.Nil(t, err)
pbytes1 := pk1.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal([]StackItem{NewByteArrayItem(sig1), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGNoHash(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1, err := pk1.Sign(msg)
assert.Nil(t, err)
sig2, err := pk2.Sign(msg)
assert.Nil(t, err)
pbytes1 := pk1.PublicKey().Bytes()
pbytes2 := pk2.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal([]StackItem{NewByteArrayItem(sig1), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1), NewByteArrayItem(pbytes2)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGBadKey(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1, err := pk1.Sign(msg)
assert.Nil(t, err)
sig2, err := pk2.Sign(msg)
assert.Nil(t, err)
pbytes1 := pk1.PublicKey().Bytes()
pbytes2 := pk2.PublicKey().Bytes()[:4]
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal([]StackItem{NewByteArrayItem(sig1), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1), NewByteArrayItem(pbytes2)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGBadSig(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1, err := pk1.Sign(msg)
assert.Nil(t, err)
sig2, err := pk2.Sign(msg)
assert.Nil(t, err)
pbytes1 := pk1.PublicKey().Bytes()
pbytes2 := pk2.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal([]StackItem{NewByteArrayItem(util.ArrayReverse(sig1)), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1), NewByteArrayItem(pbytes2)})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestCHECKMULTISIGGood(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1, err := pk1.Sign(msg)
assert.Nil(t, err)
sig2, err := pk2.Sign(msg)
assert.Nil(t, err)
pbytes1 := pk1.PublicKey().Bytes()
pbytes2 := pk2.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal([]StackItem{NewByteArrayItem(sig1), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1), NewByteArrayItem(pbytes2)})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestSWAPGood(t *testing.T) {
prog := makeProgram(opcode.SWAP)
vm := load(prog)
vm.estack.PushVal(2)
vm.estack.PushVal(4)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(4), vm.estack.Pop().BigInt().Int64())
}
func TestSWAPBad1(t *testing.T) {
prog := makeProgram(opcode.SWAP)
vm := load(prog)
vm.estack.PushVal(4)
checkVMFailed(t, vm)
}
func TestSWAPBad2(t *testing.T) {
prog := makeProgram(opcode.SWAP)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestXSWAPGood(t *testing.T) {
prog := makeProgram(opcode.XSWAP)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
vm.estack.PushVal(4)
vm.estack.PushVal(5)
vm.estack.PushVal(3)
runVM(t, vm)
assert.Equal(t, 5, vm.estack.Len())
assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(4), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(5), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
}
func TestXSWAPBad1(t *testing.T) {
prog := makeProgram(opcode.XSWAP)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(-1)
checkVMFailed(t, vm)
}
func TestXSWAPBad2(t *testing.T) {
prog := makeProgram(opcode.XSWAP)
vm := load(prog)
vm.estack.PushVal(1)
vm.estack.PushVal(2)
vm.estack.PushVal(3)
vm.estack.PushVal(4)
vm.estack.PushVal(4)
checkVMFailed(t, vm)
}
func TestDupInt(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.ABS)
vm := load(prog)
vm.estack.PushVal(-1)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
assert.Equal(t, int64(-1), vm.estack.Pop().BigInt().Int64())
}
func TestDupByteArray(t *testing.T) {
prog := makeProgram(opcode.PUSHBYTES2, 1, 0,
opcode.DUP,
opcode.PUSH1,
opcode.LEFT,
opcode.PUSHBYTES1, 2,
opcode.CAT)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, []byte{0x01, 0x02}, vm.estack.Pop().Bytes())
assert.Equal(t, []byte{0x01, 0x00}, vm.estack.Pop().Bytes())
}
func TestDupBool(t *testing.T) {
prog := makeProgram(opcode.PUSH0, opcode.NOT,
opcode.DUP,
opcode.PUSH1, opcode.NOT,
opcode.BOOLAND)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestSHA1(t *testing.T) {
// 0x0100 hashes to 0e356ba505631fbf715758bed27d503f8b260e3a
res := "0e356ba505631fbf715758bed27d503f8b260e3a"
prog := makeProgram(opcode.PUSHBYTES2, 1, 0,
opcode.SHA1)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, res, hex.EncodeToString(vm.estack.Pop().Bytes()))
}
func TestSHA256(t *testing.T) {
// 0x0100 hashes to 47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254
res := "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254"
prog := makeProgram(opcode.PUSHBYTES2, 1, 0,
opcode.SHA256)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, res, hex.EncodeToString(vm.estack.Pop().Bytes()))
}
func TestHASH160(t *testing.T) {
// 0x0100 hashes to fbc22d517f38e7612798ece8e5957cf6c41d8caf
res := "fbc22d517f38e7612798ece8e5957cf6c41d8caf"
prog := makeProgram(opcode.PUSHBYTES2, 1, 0,
opcode.HASH160)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, res, hex.EncodeToString(vm.estack.Pop().Bytes()))
}
func TestHASH256(t *testing.T) {
// 0x0100 hashes to 677b2d718464ee0121475600b929c0b4155667486577d1320b18c2dc7d4b4f99
res := "677b2d718464ee0121475600b929c0b4155667486577d1320b18c2dc7d4b4f99"
prog := makeProgram(opcode.PUSHBYTES2, 1, 0,
opcode.HASH256)
vm := load(prog)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, res, hex.EncodeToString(vm.estack.Pop().Bytes()))
}
func makeProgram(opcodes ...opcode.Opcode) []byte {
prog := make([]byte, len(opcodes)+1) // RET
for i := 0; i < len(opcodes); i++ {
prog[i] = byte(opcodes[i])
}
prog[len(prog)-1] = byte(opcode.RET)
return prog
}
func load(prog []byte) *VM {
vm := New()
vm.LoadScript(prog)
return vm
}
func randomBytes(n int) []byte {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, n)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return b
}