122 lines
3.9 KiB
Go
122 lines
3.9 KiB
Go
|
package vm
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math/big"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
type arrayIterator struct {
|
||
|
index int
|
||
|
values []stackitem.Item
|
||
|
}
|
||
|
|
||
|
func TestCreateCallAndUnwrapIteratorScript(t *testing.T) {
|
||
|
ctrHash := random.Uint160()
|
||
|
ctrMethod := "mymethod"
|
||
|
param := stackitem.NewBigInteger(big.NewInt(42))
|
||
|
|
||
|
const totalItems = 8
|
||
|
values := make([]stackitem.Item, totalItems)
|
||
|
for i := range values {
|
||
|
values[i] = stackitem.NewBigInteger(big.NewInt(int64(i)))
|
||
|
}
|
||
|
|
||
|
checkStack := func(t *testing.T, script []byte, index int, prefetch bool) {
|
||
|
v := load(script)
|
||
|
it := &arrayIterator{index: -1, values: values}
|
||
|
v.SyscallHandler = func(v *VM, id uint32) error {
|
||
|
switch id {
|
||
|
case interopnames.ToID([]byte(interopnames.SystemContractCall)):
|
||
|
require.Equal(t, ctrHash.BytesBE(), v.Estack().Pop().Value())
|
||
|
require.Equal(t, []byte(ctrMethod), v.Estack().Pop().Value())
|
||
|
require.Equal(t, big.NewInt(int64(callflag.All)), v.Estack().Pop().Value())
|
||
|
require.Equal(t, []stackitem.Item{param}, v.Estack().Pop().Value())
|
||
|
v.Estack().PushItem(stackitem.NewInterop(it))
|
||
|
case interopnames.ToID([]byte(interopnames.SystemIteratorNext)):
|
||
|
require.Equal(t, it, v.Estack().Pop().Value())
|
||
|
it.index++
|
||
|
v.Estack().PushVal(it.index < len(it.values))
|
||
|
case interopnames.ToID([]byte(interopnames.SystemIteratorValue)):
|
||
|
require.Equal(t, it, v.Estack().Pop().Value())
|
||
|
v.Estack().PushVal(it.values[it.index])
|
||
|
default:
|
||
|
return fmt.Errorf("unexpected syscall: %d", id)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
require.NoError(t, v.Run())
|
||
|
|
||
|
if prefetch && index <= len(values) {
|
||
|
require.Equal(t, 2, v.Estack().Len())
|
||
|
|
||
|
it, ok := v.Estack().Pop().Interop().Value().(*arrayIterator)
|
||
|
require.True(t, ok)
|
||
|
require.Equal(t, index-1, it.index)
|
||
|
require.Equal(t, values[:index], v.Estack().Pop().Array())
|
||
|
return
|
||
|
}
|
||
|
if len(values) < index {
|
||
|
index = len(values)
|
||
|
}
|
||
|
require.Equal(t, 1, v.Estack().Len())
|
||
|
require.Equal(t, values[:index], v.Estack().Pop().Array())
|
||
|
}
|
||
|
|
||
|
t.Run("truncate", func(t *testing.T) {
|
||
|
t.Run("zero", func(t *testing.T) {
|
||
|
const index = 0
|
||
|
script, err := smartcontract.CreateCallAndUnwrapIteratorScript(ctrHash, ctrMethod, index, param)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
// The behaviour is a bit unexpected, but not a problem (why would anyone fetch 0 items).
|
||
|
// Let's have test, to make it obvious.
|
||
|
checkStack(t, script, index+1, false)
|
||
|
})
|
||
|
t.Run("all", func(t *testing.T) {
|
||
|
const index = totalItems + 1
|
||
|
script, err := smartcontract.CreateCallAndUnwrapIteratorScript(ctrHash, ctrMethod, index, param)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
checkStack(t, script, index, false)
|
||
|
})
|
||
|
t.Run("partial", func(t *testing.T) {
|
||
|
const index = totalItems / 2
|
||
|
script, err := smartcontract.CreateCallAndUnwrapIteratorScript(ctrHash, ctrMethod, index, param)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
checkStack(t, script, index, false)
|
||
|
})
|
||
|
})
|
||
|
t.Run("prefetch", func(t *testing.T) {
|
||
|
t.Run("zero", func(t *testing.T) {
|
||
|
const index = 0
|
||
|
script, err := smartcontract.CreateCallAndPrefetchIteratorScript(ctrHash, ctrMethod, index, param)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
checkStack(t, script, index+1, true)
|
||
|
})
|
||
|
t.Run("all", func(t *testing.T) {
|
||
|
const index = totalItems + 1 // +1 to test with iterator dropped
|
||
|
script, err := smartcontract.CreateCallAndPrefetchIteratorScript(ctrHash, ctrMethod, index, param)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
checkStack(t, script, index, true)
|
||
|
})
|
||
|
t.Run("partial", func(t *testing.T) {
|
||
|
const index = totalItems / 2
|
||
|
script, err := smartcontract.CreateCallAndPrefetchIteratorScript(ctrHash, ctrMethod, index, param)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
checkStack(t, script, index, true)
|
||
|
})
|
||
|
})
|
||
|
}
|