Merge pull request #1013 from nspcc-dev/neo3/vm/stackitem_refactoring

vm: move StackItem to a separate package
This commit is contained in:
Roman Khimov 2020-06-08 15:30:44 +03:00 committed by GitHub
commit 795523f5cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 2575 additions and 2466 deletions

View file

@ -17,6 +17,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
) )
@ -720,7 +721,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// For now we will assume that there are only byte slice conversions. // For now we will assume that there are only byte slice conversions.
// E.g. []byte("foobar") or []byte(scriptHash). // E.g. []byte("foobar") or []byte(scriptHash).
ast.Walk(c, n.Args[0]) ast.Walk(c, n.Args[0])
c.emitConvert(vm.BufferT) c.emitConvert(stackitem.BufferT)
return nil return nil
} }
@ -1091,12 +1092,12 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
c.prog.Err = errors.New("panic should have string or nil argument") c.prog.Err = errors.New("panic should have string or nil argument")
} }
case "ToInteger", "ToByteArray", "ToBool": case "ToInteger", "ToByteArray", "ToBool":
typ := vm.IntegerT typ := stackitem.IntegerT
switch name { switch name {
case "ToByteArray": case "ToByteArray":
typ = vm.ByteArrayT typ = stackitem.ByteArrayT
case "ToBool": case "ToBool":
typ = vm.BooleanT typ = stackitem.BooleanT
} }
c.emitConvert(typ) c.emitConvert(typ)
case "SHA256": case "SHA256":
@ -1123,7 +1124,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
} }
bytes := uint160.BytesBE() bytes := uint160.BytesBE()
emit.Bytes(c.prog.BinWriter, bytes) emit.Bytes(c.prog.BinWriter, bytes)
c.emitConvert(vm.BufferT) c.emitConvert(stackitem.BufferT)
} }
} }
@ -1150,7 +1151,7 @@ func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
} }
// emitConvert converts top stack item to the specified type. // emitConvert converts top stack item to the specified type.
func (c *codegen) emitConvert(typ vm.StackItemType) { func (c *codegen) emitConvert(typ stackitem.Type) {
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)}) emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
} }
@ -1162,7 +1163,7 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
buf[i] = byte(val) buf[i] = byte(val)
} }
emit.Bytes(c.prog.BinWriter, buf) emit.Bytes(c.prog.BinWriter, buf)
c.emitConvert(vm.BufferT) c.emitConvert(stackitem.BufferT)
} }
func (c *codegen) convertMap(lit *ast.CompositeLit) { func (c *codegen) convertMap(lit *ast.CompositeLit) {

View file

@ -5,7 +5,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
func TestEntryPointWithMethod(t *testing.T) { func TestEntryPointWithMethod(t *testing.T) {
@ -30,7 +30,7 @@ func TestEntryPointWithArgs(t *testing.T) {
return 2 + args[1].(int) return 2 + args[1].(int)
} }
` `
args := []vm.StackItem{vm.NewBigIntegerItem(big.NewInt(0)), vm.NewBigIntegerItem(big.NewInt(1))} args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))}
evalWithArgs(t, src, nil, args, big.NewInt(3)) evalWithArgs(t, src, nil, args, big.NewInt(3))
} }
@ -45,7 +45,7 @@ func TestEntryPointWithMethodAndArgs(t *testing.T) {
return 0 return 0
} }
` `
args := []vm.StackItem{vm.NewBigIntegerItem(big.NewInt(0)), vm.NewBigIntegerItem(big.NewInt(1))} args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))}
evalWithArgs(t, src, []byte("foobar"), args, big.NewInt(3)) evalWithArgs(t, src, []byte("foobar"), args, big.NewInt(3))
} }
@ -134,10 +134,10 @@ func TestStringArray(t *testing.T) {
return x return x
} }
` `
eval(t, src, []vm.StackItem{ eval(t, src, []stackitem.Item{
vm.NewByteArrayItem([]byte("foo")), stackitem.NewByteArray([]byte("foo")),
vm.NewByteArrayItem([]byte("bar")), stackitem.NewByteArray([]byte("bar")),
vm.NewByteArrayItem([]byte("foobar")), stackitem.NewByteArray([]byte("foobar")),
}) })
} }
@ -149,10 +149,10 @@ func TestIntArray(t *testing.T) {
return arr return arr
} }
` `
eval(t, src, []vm.StackItem{ eval(t, src, []stackitem.Item{
vm.NewBigIntegerItem(big.NewInt(1)), stackitem.NewBigInteger(big.NewInt(1)),
vm.NewBigIntegerItem(big.NewInt(2)), stackitem.NewBigInteger(big.NewInt(2)),
vm.NewBigIntegerItem(big.NewInt(3)), stackitem.NewBigInteger(big.NewInt(3)),
}) })
} }
@ -198,7 +198,7 @@ func TestSimpleString(t *testing.T) {
return x return x
} }
` `
eval(t, src, vm.NewByteArrayItem([]byte("NEO")).Value()) eval(t, src, stackitem.NewByteArray([]byte("NEO")).Value())
} }
func TestBoolAssign(t *testing.T) { func TestBoolAssign(t *testing.T) {
@ -315,7 +315,7 @@ func TestAppendString(t *testing.T) {
return arr[3] return arr[3]
} }
` `
eval(t, src, vm.NewByteArrayItem([]byte("d")).Value()) eval(t, src, stackitem.NewByteArray([]byte("d")).Value())
} }
func TestAppendInt(t *testing.T) { func TestAppendInt(t *testing.T) {

View file

@ -4,7 +4,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
var sliceTestCases = []testCase{ var sliceTestCases = []testCase{
@ -160,9 +160,9 @@ var sliceTestCases = []testCase{
a = append(a, "b") a = append(a, "b")
return a return a
}`, }`,
[]vm.StackItem{ []stackitem.Item{
vm.NewByteArrayItem([]byte("a")), stackitem.NewByteArray([]byte("a")),
vm.NewByteArrayItem([]byte("b")), stackitem.NewByteArray([]byte("b")),
}, },
}, },
{ {
@ -175,9 +175,9 @@ var sliceTestCases = []testCase{
a = append(a, "b") a = append(a, "b")
return a return a
}`, }`,
[]vm.StackItem{ []stackitem.Item{
vm.NewByteArrayItem([]byte("a")), stackitem.NewByteArray([]byte("a")),
vm.NewByteArrayItem([]byte("b")), stackitem.NewByteArray([]byte("b")),
}, },
}, },
{ {

View file

@ -4,7 +4,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
var structTestCases = []testCase{ var structTestCases = []testCase{
@ -281,11 +281,11 @@ var structTestCases = []testCase{
return newToken() return newToken()
} }
`, `,
[]vm.StackItem{ []stackitem.Item{
vm.NewBigIntegerItem(big.NewInt(1)), stackitem.NewBigInteger(big.NewInt(1)),
vm.NewBigIntegerItem(big.NewInt(2)), stackitem.NewBigInteger(big.NewInt(2)),
vm.NewByteArrayItem([]byte("hello")), stackitem.NewByteArray([]byte("hello")),
vm.NewBoolItem(false), stackitem.NewBool(false),
}, },
}, },
{ {

View file

@ -4,7 +4,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -41,8 +41,8 @@ func TestNotify(t *testing.T) {
require.NoError(t, v.Run()) require.NoError(t, v.Run())
require.Equal(t, 3, len(s.events)) require.Equal(t, 3, len(s.events))
exp0 := []vm.StackItem{vm.NewBigIntegerItem(big.NewInt(11)), vm.NewByteArrayItem([]byte("sum")), vm.NewBigIntegerItem(big.NewInt(12))} exp0 := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(11)), stackitem.NewByteArray([]byte("sum")), stackitem.NewBigInteger(big.NewInt(12))}
assert.Equal(t, exp0, s.events[0].Value()) assert.Equal(t, exp0, s.events[0].Value())
assert.Equal(t, []vm.StackItem{}, s.events[1].Value()) assert.Equal(t, []stackitem.Item{}, s.events[1].Value())
assert.Equal(t, []vm.StackItem{vm.NewByteArrayItem([]byte("single"))}, s.events[2].Value()) assert.Equal(t, []stackitem.Item{stackitem.NewByteArray([]byte("single"))}, s.events[2].Value())
} }

View file

@ -8,6 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -38,7 +39,7 @@ func eval(t *testing.T, src string, result interface{}) {
assertResult(t, vm, result) assertResult(t, vm, result)
} }
func evalWithArgs(t *testing.T, src string, op []byte, args []vm.StackItem, result interface{}) { func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, result interface{}) {
vm := vmAndCompile(t, src) vm := vmAndCompile(t, src)
vm.LoadArgs(op, args) vm.LoadArgs(op, args)
err := vm.Run() err := vm.Run()
@ -73,7 +74,7 @@ func vmAndCompileInterop(t *testing.T, src string) (*vm.VM, *storagePlugin) {
type storagePlugin struct { type storagePlugin struct {
mem map[string][]byte mem map[string][]byte
interops map[uint32]vm.InteropFunc interops map[uint32]vm.InteropFunc
events []vm.StackItem events []stackitem.Item
} }
func newStoragePlugin() *storagePlugin { func newStoragePlugin() *storagePlugin {

View file

@ -18,11 +18,13 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -573,7 +575,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
return errors.Wrap(err, "failed to persist invocation results") return errors.Wrap(err, "failed to persist invocation results")
} }
for _, note := range systemInterop.Notifications { for _, note := range systemInterop.Notifications {
arr, ok := note.Item.Value().([]vm.StackItem) arr, ok := note.Item.Value().([]stackitem.Item)
if !ok || len(arr) != 4 { if !ok || len(arr) != 4 {
continue continue
} }
@ -605,7 +607,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if !ok { if !ok {
continue continue
} }
amount = emit.BytesToInt(bs) amount = bigint.FromBytes(bs)
} }
bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64()) bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64())
} }

View file

@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -60,7 +61,7 @@ type Function struct {
} }
// Method is a signature for a native method. // Method is a signature for a native method.
type Method = func(ic *Context, args []vm.StackItem) vm.StackItem type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
// MethodAndPrice is a native-contract method descriptor. // MethodAndPrice is a native-contract method descriptor.
type MethodAndPrice struct { type MethodAndPrice struct {

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// ECDSAVerify checks ECDSA signature. // ECDSAVerify checks ECDSA signature.
@ -48,12 +49,12 @@ func ECDSACheckMultisig(ic *interop.Context, v *vm.VM) error {
return nil return nil
} }
func getMessage(ic *interop.Context, item vm.StackItem) []byte { func getMessage(ic *interop.Context, item stackitem.Item) []byte {
var msg []byte var msg []byte
switch val := item.(type) { switch val := item.(type) {
case *vm.InteropItem: case *stackitem.Interop:
msg = val.Value().(crypto.Verifiable).GetSignedPart() msg = val.Value().(crypto.Verifiable).GetSignedPart()
case vm.NullItem: case stackitem.Null:
msg = ic.Container.GetSignedPart() msg = ic.Container.GetSignedPart()
default: default:
var err error var err error

View file

@ -9,16 +9,17 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func initCHECKMULTISIG(msg []byte, n int) ([]vm.StackItem, []vm.StackItem, map[string]*keys.PublicKey, error) { func initCHECKMULTISIG(msg []byte, n int) ([]stackitem.Item, []stackitem.Item, map[string]*keys.PublicKey, error) {
var err error var err error
keyMap := make(map[string]*keys.PublicKey) keyMap := make(map[string]*keys.PublicKey)
pkeys := make([]*keys.PrivateKey, n) pkeys := make([]*keys.PrivateKey, n)
pubs := make([]vm.StackItem, n) pubs := make([]stackitem.Item, n)
for i := range pubs { for i := range pubs {
pkeys[i], err = keys.NewPrivateKey() pkeys[i], err = keys.NewPrivateKey()
if err != nil { if err != nil {
@ -27,25 +28,25 @@ func initCHECKMULTISIG(msg []byte, n int) ([]vm.StackItem, []vm.StackItem, map[s
pk := pkeys[i].PublicKey() pk := pkeys[i].PublicKey()
data := pk.Bytes() data := pk.Bytes()
pubs[i] = vm.NewByteArrayItem(data) pubs[i] = stackitem.NewByteArray(data)
keyMap[string(data)] = pk keyMap[string(data)] = pk
} }
sigs := make([]vm.StackItem, n) sigs := make([]stackitem.Item, n)
for i := range sigs { for i := range sigs {
sig := pkeys[i].Sign(msg) sig := pkeys[i].Sign(msg)
sigs[i] = vm.NewByteArrayItem(sig) sigs[i] = stackitem.NewByteArray(sig)
} }
return pubs, sigs, keyMap, nil return pubs, sigs, keyMap, nil
} }
func subSlice(arr []vm.StackItem, indices []int) []vm.StackItem { func subSlice(arr []stackitem.Item, indices []int) []stackitem.Item {
if indices == nil { if indices == nil {
return arr return arr
} }
result := make([]vm.StackItem, len(indices)) result := make([]stackitem.Item, len(indices))
for i, j := range indices { for i, j := range indices {
result[i] = arr[j] result[i] = arr[j]
} }

View file

@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
const ( const (
@ -66,9 +67,9 @@ func txGetAttributes(ic *interop.Context, v *vm.VM) error {
if len(tx.Attributes) > vm.MaxArraySize { if len(tx.Attributes) > vm.MaxArraySize {
return errors.New("too many attributes") return errors.New("too many attributes")
} }
attrs := make([]vm.StackItem, 0, len(tx.Attributes)) attrs := make([]stackitem.Item, 0, len(tx.Attributes))
for i := range tx.Attributes { for i := range tx.Attributes {
attrs = append(attrs, vm.NewInteropItem(&tx.Attributes[i])) attrs = append(attrs, stackitem.NewInterop(&tx.Attributes[i]))
} }
v.Estack().PushVal(attrs) v.Estack().PushVal(attrs)
return nil return nil
@ -84,9 +85,9 @@ func txGetWitnesses(ic *interop.Context, v *vm.VM) error {
if len(tx.Scripts) > vm.MaxArraySize { if len(tx.Scripts) > vm.MaxArraySize {
return errors.New("too many outputs") return errors.New("too many outputs")
} }
scripts := make([]vm.StackItem, 0, len(tx.Scripts)) scripts := make([]stackitem.Item, 0, len(tx.Scripts))
for i := range tx.Scripts { for i := range tx.Scripts {
scripts = append(scripts, vm.NewInteropItem(&tx.Scripts[i])) scripts = append(scripts, stackitem.NewInterop(&tx.Scripts[i]))
} }
v.Estack().PushVal(scripts) v.Estack().PushVal(scripts)
return nil return nil
@ -139,7 +140,7 @@ func bcGetAccount(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
return err return err
} }
v.Estack().PushVal(vm.NewInteropItem(acc)) v.Estack().PushVal(stackitem.NewInterop(acc))
return nil return nil
} }
@ -204,13 +205,13 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
return err return err
} }
filteredMap := vm.NewMapItem() filteredMap := stackitem.NewMap()
for k, v := range siMap { for k, v := range siMap {
filteredMap.Add(vm.NewByteArrayItem(append(prefix, []byte(k)...)), vm.NewByteArrayItem(v.Value)) filteredMap.Add(stackitem.NewByteArray(append(prefix, []byte(k)...)), stackitem.NewByteArray(v.Value))
} }
sort.Slice(filteredMap.Value().([]vm.MapElement), func(i, j int) bool { sort.Slice(filteredMap.Value().([]stackitem.MapElement), func(i, j int) bool {
return bytes.Compare(filteredMap.Value().([]vm.MapElement)[i].Key.Value().([]byte), return bytes.Compare(filteredMap.Value().([]stackitem.MapElement)[i].Key.Value().([]byte),
filteredMap.Value().([]vm.MapElement)[j].Key.Value().([]byte)) == -1 filteredMap.Value().([]stackitem.MapElement)[j].Key.Value().([]byte)) == -1
}) })
item := vm.NewMapIterator(filteredMap) item := vm.NewMapIterator(filteredMap)
@ -288,7 +289,7 @@ func contractCreate(ic *interop.Context, v *vm.VM) error {
return err return err
} }
} }
v.Estack().PushVal(vm.NewInteropItem(contract)) v.Estack().PushVal(stackitem.NewInterop(contract))
return nil return nil
} }
@ -342,7 +343,7 @@ func contractMigrate(ic *interop.Context, v *vm.VM) error {
} }
} }
} }
v.Estack().PushVal(vm.NewInteropItem(contract)) v.Estack().PushVal(stackitem.NewInterop(contract))
return contractDestroy(ic, v) return contractDestroy(ic, v)
} }

View file

@ -21,6 +21,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -70,12 +71,12 @@ func TestStorageFind(t *testing.T) {
t.Run("normal invocation", func(t *testing.T) { t.Run("normal invocation", func(t *testing.T) {
v.Estack().PushVal([]byte{0x01}) v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: scriptHash})) v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: scriptHash}))
err := storageFind(context, v) err := storageFind(context, v)
require.NoError(t, err) require.NoError(t, err)
var iter *vm.InteropItem var iter *stackitem.Interop
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() }) require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
require.NoError(t, enumerator.Next(context, v)) require.NoError(t, enumerator.Next(context, v))
@ -108,7 +109,7 @@ func TestStorageFind(t *testing.T) {
t.Run("normal invocation, empty result", func(t *testing.T) { t.Run("normal invocation, empty result", func(t *testing.T) {
v.Estack().PushVal([]byte{0x03}) v.Estack().PushVal([]byte{0x03})
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: scriptHash})) v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: scriptHash}))
err := storageFind(context, v) err := storageFind(context, v)
require.NoError(t, err) require.NoError(t, err)
@ -119,7 +120,7 @@ func TestStorageFind(t *testing.T) {
t.Run("invalid type for StorageContext", func(t *testing.T) { t.Run("invalid type for StorageContext", func(t *testing.T) {
v.Estack().PushVal([]byte{0x01}) v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(vm.NewInteropItem(nil)) v.Estack().PushVal(stackitem.NewInterop(nil))
require.Error(t, storageFind(context, v)) require.Error(t, storageFind(context, v))
}) })
@ -129,7 +130,7 @@ func TestStorageFind(t *testing.T) {
invalidHash[0] = ^invalidHash[0] invalidHash[0] = ^invalidHash[0]
v.Estack().PushVal([]byte{0x01}) v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: invalidHash})) v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: invalidHash}))
require.Error(t, storageFind(context, v)) require.Error(t, storageFind(context, v))
}) })
@ -151,7 +152,7 @@ func TestHeaderGetVersion_Negative(t *testing.T) {
chain := newTestChain(t) chain := newTestChain(t)
defer chain.Close() defer chain.Close()
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
v.Estack().PushVal(vm.NewBoolItem(false)) v.Estack().PushVal(stackitem.NewBool(false))
err := headerGetVersion(context, v) err := headerGetVersion(context, v)
require.Errorf(t, err, "value is not a header or block") require.Errorf(t, err, "value is not a header or block")
@ -183,7 +184,7 @@ func TestTxGetAttributes(t *testing.T) {
err := txGetAttributes(context, v) err := txGetAttributes(context, v)
require.NoError(t, err) require.NoError(t, err)
value := v.Estack().Pop().Value().([]vm.StackItem) value := v.Estack().Pop().Value().([]stackitem.Item)
require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage) require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage)
} }
@ -196,7 +197,7 @@ func TestWitnessGetVerificationScript(t *testing.T) {
defer chain.Close() defer chain.Close()
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)
v.Estack().PushVal(vm.NewInteropItem(&witness)) v.Estack().PushVal(stackitem.NewInterop(&witness))
err := witnessGetVerificationScript(context, v) err := witnessGetVerificationScript(context, v)
require.NoError(t, err) require.NoError(t, err)
value := v.Estack().Pop().Value().([]byte) value := v.Estack().Pop().Value().([]byte)
@ -247,7 +248,7 @@ func TestECDSAVerify(t *testing.T) {
tx := transaction.New([]byte{0, 1, 2}, 1) tx := transaction.New([]byte{0, 1, 2}, 1)
msg := tx.GetSignedPart() msg := tx.GetSignedPart()
sign := priv.Sign(msg) sign := priv.Sign(msg)
runCase(t, false, true, sign, priv.PublicKey().Bytes(), vm.NewInteropItem(tx)) runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.NewInterop(tx))
}) })
t.Run("signed script container", func(t *testing.T) { t.Run("signed script container", func(t *testing.T) {
@ -255,7 +256,7 @@ func TestECDSAVerify(t *testing.T) {
msg := tx.GetSignedPart() msg := tx.GetSignedPart()
sign := priv.Sign(msg) sign := priv.Sign(msg)
ic.Container = tx ic.Container = tx
runCase(t, false, true, sign, priv.PublicKey().Bytes(), vm.NullItem{}) runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.Null{})
}) })
t.Run("missing arguments", func(t *testing.T) { t.Run("missing arguments", func(t *testing.T) {
@ -282,7 +283,7 @@ func TestECDSAVerify(t *testing.T) {
func TestAttrGetData(t *testing.T) { func TestAttrGetData(t *testing.T) {
v, tx, context, chain := createVMAndTX(t) v, tx, context, chain := createVMAndTX(t)
defer chain.Close() defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0])) v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))
err := attrGetData(context, v) err := attrGetData(context, v)
require.NoError(t, err) require.NoError(t, err)
@ -293,7 +294,7 @@ func TestAttrGetData(t *testing.T) {
func TestAttrGetUsage(t *testing.T) { func TestAttrGetUsage(t *testing.T) {
v, tx, context, chain := createVMAndTX(t) v, tx, context, chain := createVMAndTX(t)
defer chain.Close() defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0])) v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))
err := attrGetUsage(context, v) err := attrGetUsage(context, v)
require.NoError(t, err) require.NoError(t, err)
@ -304,7 +305,7 @@ func TestAttrGetUsage(t *testing.T) {
func TestAccountGetScriptHash(t *testing.T) { func TestAccountGetScriptHash(t *testing.T) {
v, accState, context, chain := createVMAndAccState(t) v, accState, context, chain := createVMAndAccState(t)
defer chain.Close() defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(accState)) v.Estack().PushVal(stackitem.NewInterop(accState))
err := accountGetScriptHash(context, v) err := accountGetScriptHash(context, v)
require.NoError(t, err) require.NoError(t, err)
@ -315,7 +316,7 @@ func TestAccountGetScriptHash(t *testing.T) {
func TestContractGetScript(t *testing.T) { func TestContractGetScript(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t) v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close() defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(contractState)) v.Estack().PushVal(stackitem.NewInterop(contractState))
err := contractGetScript(context, v) err := contractGetScript(context, v)
require.NoError(t, err) require.NoError(t, err)
@ -326,7 +327,7 @@ func TestContractGetScript(t *testing.T) {
func TestContractIsPayable(t *testing.T) { func TestContractIsPayable(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t) v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close() defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(contractState)) v.Estack().PushVal(stackitem.NewInterop(contractState))
err := contractIsPayable(context, v) err := contractIsPayable(context, v)
require.NoError(t, err) require.NoError(t, err)
@ -341,13 +342,13 @@ func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context,
block := newDumbBlock() block := newDumbBlock()
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
v.Estack().PushVal(vm.NewInteropItem(block)) v.Estack().PushVal(stackitem.NewInterop(block))
return v, block, context, chain return v, block, context, chain
} }
func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) { func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
v, tx, context, chain := createVMAndTX(t) v, tx, context, chain := createVMAndTX(t)
v.Estack().PushVal(vm.NewInteropItem(tx)) v.Estack().PushVal(stackitem.NewInterop(tx))
return v, tx, context, chain return v, tx, context, chain
} }

View file

@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -58,7 +59,7 @@ func bcGetBlock(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
v.Estack().PushVal([]byte{}) v.Estack().PushVal([]byte{})
} else { } else {
v.Estack().PushVal(vm.NewInteropItem(block)) v.Estack().PushVal(stackitem.NewInterop(block))
} }
return nil return nil
} }
@ -74,7 +75,7 @@ func bcGetContract(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
v.Estack().PushVal([]byte{}) v.Estack().PushVal([]byte{})
} else { } else {
v.Estack().PushVal(vm.NewInteropItem(cs)) v.Estack().PushVal(stackitem.NewInterop(cs))
} }
return nil return nil
} }
@ -89,7 +90,7 @@ func bcGetHeader(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
v.Estack().PushVal([]byte{}) v.Estack().PushVal([]byte{})
} else { } else {
v.Estack().PushVal(vm.NewInteropItem(header)) v.Estack().PushVal(stackitem.NewInterop(header))
} }
return nil return nil
} }
@ -117,7 +118,7 @@ func bcGetTransaction(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
return err return err
} }
v.Estack().PushVal(vm.NewInteropItem(tx)) v.Estack().PushVal(stackitem.NewInterop(tx))
return nil return nil
} }
@ -208,9 +209,9 @@ func blockGetTransactions(ic *interop.Context, v *vm.VM) error {
if len(block.Transactions) > vm.MaxArraySize { if len(block.Transactions) > vm.MaxArraySize {
return errors.New("too many transactions") return errors.New("too many transactions")
} }
txes := make([]vm.StackItem, 0, len(block.Transactions)) txes := make([]stackitem.Item, 0, len(block.Transactions))
for _, tx := range block.Transactions { for _, tx := range block.Transactions {
txes = append(txes, vm.NewInteropItem(tx)) txes = append(txes, stackitem.NewInterop(tx))
} }
v.Estack().PushVal(txes) v.Estack().PushVal(txes)
return nil return nil
@ -229,7 +230,7 @@ func blockGetTransaction(ic *interop.Context, v *vm.VM) error {
return errors.New("wrong transaction index") return errors.New("wrong transaction index")
} }
tx := block.Transactions[index] tx := block.Transactions[index]
v.Estack().PushVal(vm.NewInteropItem(tx)) v.Estack().PushVal(stackitem.NewInterop(tx))
return nil return nil
} }
@ -247,7 +248,7 @@ func txGetHash(ic *interop.Context, v *vm.VM) error {
// engineGetScriptContainer returns transaction that contains the script being // engineGetScriptContainer returns transaction that contains the script being
// run. // run.
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error { func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
v.Estack().PushVal(vm.NewInteropItem(ic.Container)) v.Estack().PushVal(stackitem.NewInterop(ic.Container))
return nil return nil
} }
@ -289,9 +290,9 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
// outside of the interop subsystem anyway. I'd probably fail transactions // outside of the interop subsystem anyway. I'd probably fail transactions
// that emit such broken notifications, but that might break compatibility // that emit such broken notifications, but that might break compatibility
// with testnet/mainnet, so we're replacing these with error messages. // with testnet/mainnet, so we're replacing these with error messages.
_, err := vm.SerializeItem(item) _, err := stackitem.SerializeItem(item)
if err != nil { if err != nil {
item = vm.NewByteArrayItem([]byte(fmt.Sprintf("bad notification: %v", err))) item = stackitem.NewByteArray([]byte(fmt.Sprintf("bad notification: %v", err)))
} }
ne := state.NotificationEvent{ScriptHash: v.GetCurrentScriptHash(), Item: item} ne := state.NotificationEvent{ScriptHash: v.GetCurrentScriptHash(), Item: item}
ic.Notifications = append(ic.Notifications, ne) ic.Notifications = append(ic.Notifications, ne)
@ -387,7 +388,7 @@ func storageGetContext(ic *interop.Context, v *vm.VM) error {
ScriptHash: v.GetCurrentScriptHash(), ScriptHash: v.GetCurrentScriptHash(),
ReadOnly: false, ReadOnly: false,
} }
v.Estack().PushVal(vm.NewInteropItem(sc)) v.Estack().PushVal(stackitem.NewInterop(sc))
return nil return nil
} }
@ -397,7 +398,7 @@ func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
ScriptHash: v.GetCurrentScriptHash(), ScriptHash: v.GetCurrentScriptHash(),
ReadOnly: true, ReadOnly: true,
} }
v.Estack().PushVal(vm.NewInteropItem(sc)) v.Estack().PushVal(stackitem.NewInterop(sc))
return nil return nil
} }
@ -467,7 +468,7 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
} }
stc = stx stc = stx
} }
v.Estack().PushVal(vm.NewInteropItem(stc)) v.Estack().PushVal(stackitem.NewInterop(stc))
return nil return nil
} }
@ -488,7 +489,7 @@ func contractCallEx(ic *interop.Context, v *vm.VM) error {
return contractCallExInternal(ic, v, h, method, args, flags) return contractCallExInternal(ic, v, h, method, args, flags)
} }
func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method vm.StackItem, args vm.StackItem, _ smartcontract.CallFlag) error { func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method stackitem.Item, args stackitem.Item, _ smartcontract.CallFlag) error {
u, err := util.Uint160DecodeBytesBE(h) u, err := util.Uint160DecodeBytesBE(h)
if err != nil { if err != nil {
return errors.New("invalid contract hash") return errors.New("invalid contract hash")
@ -548,6 +549,6 @@ func contractGetStorageContext(ic *interop.Context, v *vm.VM) error {
stc := &StorageContext{ stc := &StorageContext{
ScriptHash: cs.ScriptHash(), ScriptHash: cs.ScriptHash(),
} }
v.Estack().PushVal(vm.NewInteropItem(stc)) v.Estack().PushVal(stackitem.NewInterop(stc))
return nil return nil
} }

View file

@ -14,7 +14,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -194,7 +194,7 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
return nil return nil
} }
func (n *NEO) unclaimedGas(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem.Item {
u := toUint160(args[0]) u := toUint160(args[0])
end := uint32(toBigInt(args[1]).Int64()) end := uint32(toBigInt(args[1]).Int64())
bs, err := ic.DAO.GetNEP5Balances(u) bs, err := ic.DAO.GetNEP5Balances(u)
@ -204,12 +204,12 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []vm.StackItem) vm.StackIte
tr := bs.Trackers[n.Hash] tr := bs.Trackers[n.Hash]
gen := ic.Chain.CalculateClaimable(tr.Balance, tr.LastUpdatedBlock, end) gen := ic.Chain.CalculateClaimable(tr.Balance, tr.LastUpdatedBlock, end)
return vm.NewBigIntegerItem(big.NewInt(int64(gen))) return stackitem.NewBigInteger(big.NewInt(int64(gen)))
} }
func (n *NEO) registerValidator(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (n *NEO) registerValidator(ic *interop.Context, args []stackitem.Item) stackitem.Item {
err := n.registerValidatorInternal(ic, toPublicKey(args[0])) err := n.registerValidatorInternal(ic, toPublicKey(args[0]))
return vm.NewBoolItem(err == nil) return stackitem.NewBool(err == nil)
} }
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error { func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
@ -226,9 +226,9 @@ func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey
return ic.DAO.PutStorageItem(n.Hash, key, si) return ic.DAO.PutStorageItem(n.Hash, key, si)
} }
func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
acc := toUint160(args[0]) acc := toUint160(args[0])
arr := args[1].Value().([]vm.StackItem) arr := args[1].Value().([]stackitem.Item)
var pubs keys.PublicKeys var pubs keys.PublicKeys
for i := range arr { for i := range arr {
pub := new(keys.PublicKey) pub := new(keys.PublicKey)
@ -241,7 +241,7 @@ func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
pubs = append(pubs, pub) pubs = append(pubs, pub)
} }
err := n.VoteInternal(ic, acc, pubs) err := n.VoteInternal(ic, acc, pubs)
return vm.NewBoolItem(err == nil) return stackitem.NewBool(err == nil)
} }
// VoteInternal votes from account h for validarors specified in pubs. // VoteInternal votes from account h for validarors specified in pubs.
@ -361,19 +361,19 @@ func (n *NEO) GetRegisteredValidators(d dao.DAO) ([]state.Validator, error) {
return arr, nil return arr, nil
} }
func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []vm.StackItem) vm.StackItem { func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
validators, err := n.getRegisteredValidators(ic.DAO) validators, err := n.getRegisteredValidators(ic.DAO)
if err != nil { if err != nil {
panic(err) panic(err)
} }
arr := make([]vm.StackItem, len(validators)) arr := make([]stackitem.Item, len(validators))
for i := range validators { for i := range validators {
arr[i] = vm.NewStructItem([]vm.StackItem{ arr[i] = stackitem.NewStruct([]stackitem.Item{
vm.NewByteArrayItem([]byte(validators[i].Key)), stackitem.NewByteArray([]byte(validators[i].Key)),
vm.NewBigIntegerItem(validators[i].Votes), stackitem.NewBigInteger(validators[i].Votes),
}) })
} }
return vm.NewArrayItem(arr) return stackitem.NewArray(arr)
} }
// GetValidatorsInternal returns a list of current validators. // GetValidatorsInternal returns a list of current validators.
@ -430,7 +430,7 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke
return result, nil return result, nil
} }
func (n *NEO) getValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem { func (n *NEO) getValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
result, err := n.GetValidatorsInternal(ic.Chain, ic.DAO) result, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
if err != nil { if err != nil {
panic(err) panic(err)
@ -438,7 +438,7 @@ func (n *NEO) getValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem
return pubsToArray(result) return pubsToArray(result)
} }
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem { func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
result, err := n.GetNextBlockValidatorsInternal(ic.Chain, ic.DAO) result, err := n.GetNextBlockValidatorsInternal(ic.Chain, ic.DAO)
if err != nil { if err != nil {
panic(err) panic(err)
@ -460,15 +460,15 @@ func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao
return pubs, nil return pubs, nil
} }
func pubsToArray(pubs keys.PublicKeys) vm.StackItem { func pubsToArray(pubs keys.PublicKeys) stackitem.Item {
arr := make([]vm.StackItem, len(pubs)) arr := make([]stackitem.Item, len(pubs))
for i := range pubs { for i := range pubs {
arr[i] = vm.NewByteArrayItem(pubs[i].Bytes()) arr[i] = stackitem.NewByteArray(pubs[i].Bytes())
} }
return vm.NewArrayItem(arr) return stackitem.NewArray(arr)
} }
func toPublicKey(s vm.StackItem) *keys.PublicKey { func toPublicKey(s stackitem.Item) *keys.PublicKey {
buf, err := s.TryBytes() buf, err := s.TryBytes()
if err != nil { if err != nil {
panic(err) panic(err)

View file

@ -7,11 +7,11 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
) )
// prefixAccount is the standard prefix used to store account data. // prefixAccount is the standard prefix used to store account data.
@ -86,20 +86,20 @@ func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
return nil return nil
} }
func (c *nep5TokenNative) Name(_ *interop.Context, _ []vm.StackItem) vm.StackItem { func (c *nep5TokenNative) Name(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
return vm.NewByteArrayItem([]byte(c.name)) return stackitem.NewByteArray([]byte(c.name))
} }
func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []vm.StackItem) vm.StackItem { func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
return vm.NewByteArrayItem([]byte(c.symbol)) return stackitem.NewByteArray([]byte(c.symbol))
} }
func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.StackItem { func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
return vm.NewBigIntegerItem(big.NewInt(c.decimals)) return stackitem.NewBigInteger(big.NewInt(c.decimals))
} }
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []vm.StackItem) vm.StackItem { func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
return vm.NewBigIntegerItem(c.getTotalSupply(ic)) return stackitem.NewBigInteger(c.getTotalSupply(ic))
} }
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int { func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
@ -107,37 +107,37 @@ func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
if si == nil { if si == nil {
return big.NewInt(0) return big.NewInt(0)
} }
return emit.BytesToInt(si.Value) return bigint.FromBytes(si.Value)
} }
func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error { func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error {
si := &state.StorageItem{Value: emit.IntToBytes(supply)} si := &state.StorageItem{Value: bigint.ToBytes(supply)}
return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si) return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si)
} }
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (c *nep5TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
from := toUint160(args[0]) from := toUint160(args[0])
to := toUint160(args[1]) to := toUint160(args[1])
amount := toBigInt(args[2]) amount := toBigInt(args[2])
err := c.transfer(ic, from, to, amount) err := c.transfer(ic, from, to, amount)
return vm.NewBoolItem(err == nil) return stackitem.NewBool(err == nil)
} }
func addrToStackItem(u *util.Uint160) vm.StackItem { func addrToStackItem(u *util.Uint160) stackitem.Item {
if u == nil { if u == nil {
return vm.NullItem{} return stackitem.Null{}
} }
return vm.NewByteArrayItem(u.BytesBE()) return stackitem.NewByteArray(u.BytesBE())
} }
func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) { func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
ne := state.NotificationEvent{ ne := state.NotificationEvent{
ScriptHash: c.Hash, ScriptHash: c.Hash,
Item: vm.NewArrayItem([]vm.StackItem{ Item: stackitem.NewArray([]stackitem.Item{
vm.NewByteArrayItem([]byte("Transfer")), stackitem.NewByteArray([]byte("Transfer")),
addrToStackItem(from), addrToStackItem(from),
addrToStackItem(to), addrToStackItem(to),
vm.NewBigIntegerItem(amount), stackitem.NewBigInteger(amount),
}), }),
} }
ic.Notifications = append(ic.Notifications, ne) ic.Notifications = append(ic.Notifications, ne)
@ -197,14 +197,14 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
return nil return nil
} }
func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
h := toUint160(args[0]) h := toUint160(args[0])
bs, err := ic.DAO.GetNEP5Balances(h) bs, err := ic.DAO.GetNEP5Balances(h)
if err != nil { if err != nil {
panic(err) panic(err)
} }
balance := bs.Trackers[c.Hash].Balance balance := bs.Trackers[c.Hash].Balance
return vm.NewBigIntegerItem(big.NewInt(balance)) return stackitem.NewBigInteger(big.NewInt(balance))
} }
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) { func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
@ -269,7 +269,7 @@ func newMethodAndPrice(f interop.Method, price int64, flags smartcontract.CallFl
} }
} }
func toBigInt(s vm.StackItem) *big.Int { func toBigInt(s stackitem.Item) *big.Int {
bi, err := s.TryInteger() bi, err := s.TryInteger()
if err != nil { if err != nil {
panic(err) panic(err)
@ -277,7 +277,7 @@ func toBigInt(s vm.StackItem) *big.Int {
return bi return bi
} }
func toUint160(s vm.StackItem) util.Uint160 { func toUint160(s stackitem.Item) util.Uint160 {
buf, err := s.TryBytes() buf, err := s.TryBytes()
if err != nil { if err != nil {
panic(err) panic(err)

View file

@ -1,10 +1,11 @@
package native package native
import ( import (
"errors"
"math/big" "math/big"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
) )
// MaxValidatorsVoted limits the number of validators that one can vote for. // MaxValidatorsVoted limits the number of validators that one can vote for.
@ -41,19 +42,25 @@ func (vc *ValidatorsCount) Bytes() []byte {
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) { func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
w.WriteVarUint(uint64(MaxValidatorsVoted))
for i := range vc { for i := range vc {
w.WriteVarBytes(emit.IntToBytes(&vc[i])) w.WriteVarBytes(bigint.ToBytes(&vc[i]))
} }
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) { func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
for i := range vc { count := r.ReadVarUint()
if count < 0 || count > MaxValidatorsVoted {
r.Err = errors.New("invalid validators count")
return
}
for i := 0; i < int(count); i++ {
buf := r.ReadVarBytes() buf := r.ReadVarBytes()
if r.Err != nil { if r.Err != nil {
return return
} }
vc[i] = *emit.BytesToInt(buf) vc[i] = *bigint.FromBytes(buf)
} }
} }

View file

@ -10,8 +10,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -67,7 +67,7 @@ func newTestNative() *testNative {
return tn return tn
} }
func (tn *testNative) sum(_ *interop.Context, args []vm.StackItem) vm.StackItem { func (tn *testNative) sum(_ *interop.Context, args []stackitem.Item) stackitem.Item {
s1, err := args[0].TryInteger() s1, err := args[0].TryInteger()
if err != nil { if err != nil {
panic(err) panic(err)
@ -76,7 +76,7 @@ func (tn *testNative) sum(_ *interop.Context, args []vm.StackItem) vm.StackItem
if err != nil { if err != nil {
panic(err) panic(err)
} }
return vm.NewBigIntegerItem(s1.Add(s1, s2)) return stackitem.NewBigInteger(s1.Add(s1, s2))
} }
func TestNativeContract_Invoke(t *testing.T) { func TestNativeContract_Invoke(t *testing.T) {

View file

@ -5,7 +5,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// NEP5BalanceState represents balance state of a NEP5-token. // NEP5BalanceState represents balance state of a NEP5-token.
@ -44,18 +44,27 @@ func (s *NEP5BalanceState) Bytes() []byte {
return w.Bytes() return w.Bytes()
} }
func (s *NEP5BalanceState) toStackItem() stackitem.Item {
return stackitem.NewStruct([]stackitem.Item{stackitem.NewBigInteger(&s.Balance)})
}
func (s *NEP5BalanceState) fromStackItem(item stackitem.Item) {
s.Balance = *item.(*stackitem.Struct).Value().([]stackitem.Item)[0].Value().(*big.Int)
}
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (s *NEP5BalanceState) EncodeBinary(w *io.BinWriter) { func (s *NEP5BalanceState) EncodeBinary(w *io.BinWriter) {
w.WriteVarBytes(emit.IntToBytes(&s.Balance)) si := s.toStackItem()
stackitem.EncodeBinaryStackItem(si, w)
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) { func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) {
buf := r.ReadVarBytes() si := stackitem.DecodeBinaryStackItem(r)
if r.Err != nil { if r.Err != nil {
return return
} }
s.Balance = *emit.BytesToInt(buf) s.fromStackItem(si)
} }
// NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure. // NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure.
@ -85,14 +94,37 @@ func (s *NEOBalanceState) Bytes() []byte {
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) { func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
s.NEP5BalanceState.EncodeBinary(w) si := s.toStackItem()
w.WriteU32LE(s.BalanceHeight) stackitem.EncodeBinaryStackItem(si, w)
w.WriteArray(s.Votes)
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) { func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
s.NEP5BalanceState.DecodeBinary(r) si := stackitem.DecodeBinaryStackItem(r)
s.BalanceHeight = r.ReadU32LE() if r.Err != nil {
r.ReadArray(&s.Votes) return
}
s.fromStackItem(si)
}
func (s *NEOBalanceState) toStackItem() stackitem.Item {
result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct)
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
votes := make([]stackitem.Item, len(s.Votes))
for i, v := range s.Votes {
votes[i] = stackitem.NewByteArray(v.Bytes())
}
result.Append(stackitem.NewArray(votes))
return result
}
func (s *NEOBalanceState) fromStackItem(item stackitem.Item) {
structItem := item.Value().([]stackitem.Item)
s.Balance = *structItem[0].Value().(*big.Int)
s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64())
votes := structItem[2].Value().([]stackitem.Item)
s.Votes = make([]*keys.PublicKey, len(votes))
for i, v := range votes {
s.Votes[i].DecodeBytes(v.Value().([]byte))
}
} }

View file

@ -5,14 +5,14 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// NotificationEvent is a tuple of scripthash that emitted the StackItem as a // NotificationEvent is a tuple of scripthash that emitted the Item as a
// notification and that item itself. // notification and that item itself.
type NotificationEvent struct { type NotificationEvent struct {
ScriptHash util.Uint160 ScriptHash util.Uint160
Item vm.StackItem Item stackitem.Item
} }
// AppExecResult represent the result of the script execution, gathering together // AppExecResult represent the result of the script execution, gathering together
@ -29,13 +29,13 @@ type AppExecResult struct {
// EncodeBinary implements the Serializable interface. // EncodeBinary implements the Serializable interface.
func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) { func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) {
ne.ScriptHash.EncodeBinary(w) ne.ScriptHash.EncodeBinary(w)
vm.EncodeBinaryStackItem(ne.Item, w) stackitem.EncodeBinaryStackItem(ne.Item, w)
} }
// DecodeBinary implements the Serializable interface. // DecodeBinary implements the Serializable interface.
func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) { func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
ne.ScriptHash.DecodeBinary(r) ne.ScriptHash.DecodeBinary(r)
ne.Item = vm.DecodeBinaryStackItem(r) ne.Item = stackitem.DecodeBinaryStackItem(r)
} }
// EncodeBinary implements the Serializable interface. // EncodeBinary implements the Serializable interface.

View file

@ -6,13 +6,13 @@ import (
"github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
func TestEncodeDecodeNotificationEvent(t *testing.T) { func TestEncodeDecodeNotificationEvent(t *testing.T) {
event := &NotificationEvent{ event := &NotificationEvent{
ScriptHash: random.Uint160(), ScriptHash: random.Uint160(),
Item: vm.NewBoolItem(true), Item: stackitem.NewBool(true),
} }
testserdes.EncodeDecodeBinary(t, event, new(NotificationEvent)) testserdes.EncodeDecodeBinary(t, event, new(NotificationEvent))

View file

@ -1,4 +1,4 @@
package emit package bigint
import ( import (
"encoding/binary" "encoding/binary"
@ -9,9 +9,9 @@ import (
// wordSizeBytes is a size of a big.Word (uint) in bytes.` // wordSizeBytes is a size of a big.Word (uint) in bytes.`
const wordSizeBytes = bits.UintSize / 8 const wordSizeBytes = bits.UintSize / 8
// BytesToInt converts data in little-endian format to // FromBytes converts data in little-endian format to
// an integer. // an integer.
func BytesToInt(data []byte) *big.Int { func FromBytes(data []byte) *big.Int {
n := new(big.Int) n := new(big.Int)
size := len(data) size := len(data)
if size == 0 { if size == 0 {
@ -79,15 +79,17 @@ func getEffectiveSize(buf []byte, isNeg bool) int {
return size return size
} }
// IntToBytes converts integer to a slice in little-endian format. // ToBytes converts integer to a slice in little-endian format.
// Note: NEO3 serialization differs from default C# BigInteger.ToByteArray() // Note: NEO3 serialization differs from default C# BigInteger.ToByteArray()
// when n == 0. For zero is equal to empty slice in NEO3. // when n == 0. For zero is equal to empty slice in NEO3.
// https://github.com/neo-project/neo-vm/blob/master/src/neo-vm/Types/Integer.cs#L16 // https://github.com/neo-project/neo-vm/blob/master/src/neo-vm/Types/Integer.cs#L16
func IntToBytes(n *big.Int) []byte { func ToBytes(n *big.Int) []byte {
return intToBytes(n, []byte{}) return ToPreallocatedBytes(n, []byte{})
} }
func intToBytes(n *big.Int, data []byte) []byte { // ToPreallocatedBytes converts integer to a slice in little-endian format using given
// byte array for conversion result.
func ToPreallocatedBytes(n *big.Int, data []byte) []byte {
sign := n.Sign() sign := n.Sign()
if sign == 0 { if sign == 0 {
return data return data

View file

@ -1,4 +1,4 @@
package emit package bigint
import ( import (
"math" "math"
@ -106,19 +106,19 @@ var testCases = []struct {
func TestIntToBytes(t *testing.T) { func TestIntToBytes(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
buf := IntToBytes(big.NewInt(tc.number)) buf := ToBytes(big.NewInt(tc.number))
assert.Equal(t, tc.buf, buf, "error while converting %d", tc.number) assert.Equal(t, tc.buf, buf, "error while converting %d", tc.number)
} }
} }
func TestBytesToInt(t *testing.T) { func TestBytesToInt(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
num := BytesToInt(tc.buf) num := FromBytes(tc.buf)
assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number) assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number)
} }
t.Run("empty array", func(t *testing.T) { t.Run("empty array", func(t *testing.T) {
require.EqualValues(t, 0, BytesToInt([]byte{}).Int64()) require.EqualValues(t, 0, FromBytes([]byte{}).Int64())
}) })
} }
@ -131,7 +131,7 @@ func TestEquivalentRepresentations(t *testing.T) {
buf = append(buf, 0xFF, 0xFF, 0xFF) buf = append(buf, 0xFF, 0xFF, 0xFF)
} }
num := BytesToInt(buf) num := FromBytes(buf)
assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number) assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number)
} }
} }
@ -170,16 +170,16 @@ func TestVeryBigInts(t *testing.T) {
num, ok := new(big.Int).SetString(tc.numStr, 10) num, ok := new(big.Int).SetString(tc.numStr, 10)
assert.True(t, ok) assert.True(t, ok)
result := BytesToInt(tc.buf) result := FromBytes(tc.buf)
assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr) assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr)
assert.Equal(t, tc.buf, IntToBytes(result), "error while converting %s to bytes", tc.numStr) assert.Equal(t, tc.buf, ToBytes(result), "error while converting %s to bytes", tc.numStr)
} }
for _, tc := range stdlibCases { for _, tc := range stdlibCases {
num, ok := new(big.Int).SetString(tc.numStr, 10) num, ok := new(big.Int).SetString(tc.numStr, 10)
assert.True(t, ok) assert.True(t, ok)
result := BytesToInt(util.ArrayReverse(tc.buf)) result := FromBytes(util.ArrayReverse(tc.buf))
assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr) assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr)
} }
} }

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -180,7 +181,7 @@ func topIntFromStack(st []smartcontract.Parameter) (int64, error) {
if !ok { if !ok {
return 0, errors.New("invalid ByteArray item") return 0, errors.New("invalid ByteArray item")
} }
decimals = emit.BytesToInt(data).Int64() decimals = bigint.FromBytes(data).Int64()
default: default:
return 0, fmt.Errorf("invalid stack item type: %s", typ) return 0, fmt.Errorf("invalid stack item type: %s", typ)
} }

View file

@ -4,7 +4,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// ApplicationLog wrapper used for the representation of the // ApplicationLog wrapper used for the representation of the
@ -33,8 +33,8 @@ type NotificationEvent struct {
// StateEventToResultNotification converts state.NotificationEvent to // StateEventToResultNotification converts state.NotificationEvent to
// result.NotificationEvent. // result.NotificationEvent.
func StateEventToResultNotification(event state.NotificationEvent) NotificationEvent { func StateEventToResultNotification(event state.NotificationEvent) NotificationEvent {
seen := make(map[vm.StackItem]bool) seen := make(map[stackitem.Item]bool)
item := event.Item.ToContractParameter(seen) item := smartcontract.ParameterFromStackItem(event.Item, seen)
return NotificationEvent{ return NotificationEvent{
Contract: event.ScriptHash, Contract: event.ScriptHash,
Item: item, Item: item,

View file

@ -21,6 +21,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/network" "github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/rpc" "github.com/nspcc-dev/neo-go/pkg/rpc"
@ -29,7 +30,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -625,7 +625,7 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6
case smartcontract.IntegerType: case smartcontract.IntegerType:
d = item.Value.(int64) d = item.Value.(int64)
case smartcontract.ByteArrayType: case smartcontract.ByteArrayType:
d = emit.BytesToInt(item.Value.([]byte)).Int64() d = bigint.FromBytes(item.Value.([]byte)).Int64()
default: default:
return 0, response.NewInternalServerError("invalid result", errors.New("not an integer")) return 0, response.NewInternalServerError("invalid result", errors.New("not an integer"))
} }

View file

@ -0,0 +1,72 @@
package smartcontract
import (
"fmt"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// ParameterFromStackItem converts stackitem.Item to Parameter
func ParameterFromStackItem(i stackitem.Item, seen map[stackitem.Item]bool) Parameter {
switch t := i.(type) {
case stackitem.Null, *stackitem.Pointer:
return NewParameter(AnyType)
case *stackitem.BigInteger:
return Parameter{
Type: IntegerType,
Value: i.Value().(*big.Int).Int64(),
}
case *stackitem.Bool:
return Parameter{
Type: BoolType,
Value: i.Value().(bool),
}
case *stackitem.ByteArray:
return Parameter{
Type: ByteArrayType,
Value: i.Value().([]byte),
}
case *stackitem.Interop:
return Parameter{
Type: InteropInterfaceType,
Value: nil,
}
case *stackitem.Buffer:
return Parameter{
Type: ByteArrayType,
Value: i.Value().([]byte),
}
case *stackitem.Struct, *stackitem.Array:
var value []Parameter
if !seen[i] {
seen[i] = true
for _, stackItem := range i.Value().([]stackitem.Item) {
parameter := ParameterFromStackItem(stackItem, seen)
value = append(value, parameter)
}
}
return Parameter{
Type: ArrayType,
Value: value,
}
case *stackitem.Map:
value := make([]ParameterPair, 0)
if !seen[i] {
seen[i] = true
for _, element := range i.Value().([]stackitem.MapElement) {
value = append(value, ParameterPair{
Key: ParameterFromStackItem(element.Key, seen),
Value: ParameterFromStackItem(element.Value, seen),
})
}
}
return Parameter{
Type: MapType,
Value: value,
}
default:
panic(fmt.Sprintf("unknown stack item type: %v", t))
}
}

View file

@ -0,0 +1,79 @@
package smartcontract
import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert"
)
var toContractParameterTestCases = []struct {
input stackitem.Item
result Parameter
}{
{
input: stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(1)),
stackitem.NewBool(true),
}),
result: Parameter{Type: ArrayType, Value: []Parameter{
{Type: IntegerType, Value: int64(1)},
{Type: BoolType, Value: true},
}},
},
{
input: stackitem.NewBool(false),
result: Parameter{Type: BoolType, Value: false},
},
{
input: stackitem.NewByteArray([]byte{0x01, 0x02, 0x03}),
result: Parameter{Type: ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
},
{
input: stackitem.NewBuffer([]byte{0x01, 0x02, 0x03}),
result: Parameter{Type: ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
},
{
input: stackitem.NewArray([]stackitem.Item{stackitem.NewBigInteger(big.NewInt(2)), stackitem.NewBool(true)}),
result: Parameter{Type: ArrayType, Value: []Parameter{
{Type: IntegerType, Value: int64(2)},
{Type: BoolType, Value: true},
}},
},
{
input: stackitem.NewInterop(nil),
result: Parameter{Type: InteropInterfaceType, Value: nil},
},
{
input: stackitem.NewMapWithValue([]stackitem.MapElement{
{Key: stackitem.NewBigInteger(big.NewInt(1)), Value: stackitem.NewBool(true)},
{Key: stackitem.NewByteArray([]byte("qwerty")), Value: stackitem.NewBigInteger(big.NewInt(3))},
{Key: stackitem.NewBool(true), Value: stackitem.NewBool(false)},
}),
result: Parameter{
Type: MapType,
Value: []ParameterPair{
{
Key: Parameter{Type: IntegerType, Value: int64(1)},
Value: Parameter{Type: BoolType, Value: true},
}, {
Key: Parameter{Type: ByteArrayType, Value: []byte("qwerty")},
Value: Parameter{Type: IntegerType, Value: int64(3)},
}, {
Key: Parameter{Type: BoolType, Value: true},
Value: Parameter{Type: BoolType, Value: false},
},
},
},
},
}
func TestToContractParameter(t *testing.T) {
for _, tc := range toContractParameterTestCases {
seen := make(map[stackitem.Item]bool)
res := ParameterFromStackItem(tc.input, seen)
assert.Equal(t, res, tc.result)
}
}

View file

@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"gopkg.in/abiosoft/ishell.v2" "gopkg.in/abiosoft/ishell.v2"
) )
@ -276,7 +277,7 @@ func handleRun(c *ishell.Context) {
if len(c.Args) != 0 { if len(c.Args) != 0 {
var ( var (
method []byte method []byte
params []vm.StackItem params []stackitem.Item
err error err error
) )
method = []byte(c.Args[0]) method = []byte(c.Args[0])
@ -406,8 +407,8 @@ func isMethodArg(s string) bool {
return len(strings.Split(s, ":")) == 1 return len(strings.Split(s, ":")) == 1
} }
func parseArgs(args []string) ([]vm.StackItem, error) { func parseArgs(args []string) ([]stackitem.Item, error) {
items := make([]vm.StackItem, len(args)) items := make([]stackitem.Item, len(args))
for i, arg := range args { for i, arg := range args {
var typ, value string var typ, value string
typeAndVal := strings.Split(arg, ":") typeAndVal := strings.Split(arg, ":")
@ -428,9 +429,9 @@ func parseArgs(args []string) ([]vm.StackItem, error) {
switch typ { switch typ {
case boolType: case boolType:
if value == boolFalse { if value == boolFalse {
items[i] = vm.NewBoolItem(false) items[i] = stackitem.NewBool(false)
} else if value == boolTrue { } else if value == boolTrue {
items[i] = vm.NewBoolItem(true) items[i] = stackitem.NewBool(true)
} else { } else {
return nil, errors.New("failed to parse bool parameter") return nil, errors.New("failed to parse bool parameter")
} }
@ -439,9 +440,9 @@ func parseArgs(args []string) ([]vm.StackItem, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
items[i] = vm.NewBigIntegerItem(big.NewInt(val)) items[i] = stackitem.NewBigInteger(big.NewInt(val))
case stringType: case stringType:
items[i] = vm.NewByteArrayItem([]byte(value)) items[i] = stackitem.NewByteArray([]byte(value))
} }
} }

View file

@ -6,9 +6,9 @@ import (
"math/big" "math/big"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// Context represents the current execution context of the VM. // Context represents the current execution context of the VM.
@ -170,50 +170,42 @@ func (c *Context) ScriptHash() util.Uint160 {
return c.scriptHash return c.scriptHash
} }
// Value implements StackItem interface. // Value implements stackitem.Item interface.
func (c *Context) Value() interface{} { func (c *Context) Value() interface{} {
return c return c
} }
// Dup implements StackItem interface. // Dup implements stackitem.Item interface.
func (c *Context) Dup() StackItem { func (c *Context) Dup() stackitem.Item {
return c return c
} }
// Bool implements StackItem interface. // Bool implements stackitem.Item interface.
func (c *Context) Bool() bool { panic("can't convert Context to Bool") } func (c *Context) Bool() bool { panic("can't convert Context to Bool") }
// TryBytes implements StackItem interface. // TryBytes implements stackitem.Item interface.
func (c *Context) TryBytes() ([]byte, error) { func (c *Context) TryBytes() ([]byte, error) {
return nil, errors.New("can't convert Context to ByteArray") return nil, errors.New("can't convert Context to ByteArray")
} }
// TryInteger implements StackItem interface. // TryInteger implements stackitem.Item interface.
func (c *Context) TryInteger() (*big.Int, error) { func (c *Context) TryInteger() (*big.Int, error) {
return nil, errors.New("can't convert Context to Integer") return nil, errors.New("can't convert Context to Integer")
} }
// Type implements StackItem interface. // Type implements stackitem.Item interface.
func (c *Context) Type() StackItemType { panic("Context cannot appear on evaluation stack") } func (c *Context) Type() stackitem.Type { panic("Context cannot appear on evaluation stack") }
// Convert implements StackItem interface. // Convert implements stackitem.Item interface.
func (c *Context) Convert(_ StackItemType) (StackItem, error) { func (c *Context) Convert(_ stackitem.Type) (stackitem.Item, error) {
panic("Context cannot be converted to anything") panic("Context cannot be converted to anything")
} }
// Equals implements StackItem interface. // Equals implements stackitem.Item interface.
func (c *Context) Equals(s StackItem) bool { func (c *Context) Equals(s stackitem.Item) bool {
return c == s return c == s
} }
// ToContractParameter implements StackItem interface.
func (c *Context) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
return smartcontract.Parameter{
Type: smartcontract.StringType,
Value: c.String(),
}
}
func (c *Context) atBreakPoint() bool { func (c *Context) atBreakPoint() bool {
for _, n := range c.breakPoints { for _, n := range c.breakPoints {
if n == c.ip { if n == c.ip {

View file

@ -3,6 +3,7 @@ package vm
import ( import (
"encoding/binary" "encoding/binary"
"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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
) )
@ -19,7 +20,7 @@ func getNumOfThingsFromInstr(instr opcode.Opcode, param []byte) (int, bool) {
case opcode.PUSH1 <= instr && instr <= opcode.PUSH16: case opcode.PUSH1 <= instr && instr <= opcode.PUSH16:
nthings = int(instr-opcode.PUSH1) + 1 nthings = int(instr-opcode.PUSH1) + 1
case instr <= opcode.PUSHINT256: case instr <= opcode.PUSHINT256:
n := emit.BytesToInt(param) n := bigint.FromBytes(param)
if !n.IsInt64() || n.Int64() > MaxArraySize { if !n.IsInt64() || n.Int64() > MaxArraySize {
return 0, false return 0, false
} }

View file

@ -8,9 +8,11 @@ import (
"math/big" "math/big"
"math/bits" "math/bits"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// Instruction emits a VM Instruction with data to the given buffer. // Instruction emits a VM Instruction with data to the given buffer.
@ -31,7 +33,7 @@ func Bool(w *io.BinWriter, ok bool) {
return return
} }
Opcode(w, opcode.PUSHF) Opcode(w, opcode.PUSHF)
Instruction(w, opcode.CONVERT, []byte{0x20}) // 0x20 for Boolean type Instruction(w, opcode.CONVERT, []byte{byte(stackitem.BooleanT)})
} }
func padRight(s int, buf []byte) []byte { func padRight(s int, buf []byte) []byte {
@ -54,7 +56,7 @@ func Int(w *io.BinWriter, i int64) {
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i)) val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
Opcode(w, val) Opcode(w, val)
default: default:
buf := intToBytes(big.NewInt(i), make([]byte, 0, 32)) buf := bigint.ToPreallocatedBytes(big.NewInt(i), make([]byte, 0, 32))
// l != 0 becase of switch // l != 0 becase of switch
padSize := byte(8 - bits.LeadingZeros8(byte(len(buf)-1))) padSize := byte(8 - bits.LeadingZeros8(byte(len(buf)-1)))
Opcode(w, opcode.PUSHINT8+opcode.Opcode(padSize)) Opcode(w, opcode.PUSHINT8+opcode.Opcode(padSize))

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -49,7 +50,7 @@ func TestEmitInt(t *testing.T) {
result := buf.Bytes() result := buf.Bytes()
assert.Equal(t, 3, len(result)) assert.Equal(t, 3, len(result))
assert.EqualValues(t, opcode.PUSHINT16, result[0]) assert.EqualValues(t, opcode.PUSHINT16, result[0])
assert.EqualValues(t, 300, BytesToInt(result[1:]).Int64()) assert.EqualValues(t, 300, bigint.FromBytes(result[1:]).Int64())
}) })
t.Run("3-byte int", func(t *testing.T) { t.Run("3-byte int", func(t *testing.T) {
@ -58,7 +59,7 @@ func TestEmitInt(t *testing.T) {
result := buf.Bytes() result := buf.Bytes()
assert.Equal(t, 5, len(result)) assert.Equal(t, 5, len(result))
assert.EqualValues(t, opcode.PUSHINT32, result[0]) assert.EqualValues(t, opcode.PUSHINT32, result[0])
assert.EqualValues(t, 1<<20, BytesToInt(result[1:]).Int64()) assert.EqualValues(t, 1<<20, bigint.FromBytes(result[1:]).Int64())
}) })
t.Run("4-byte int", func(t *testing.T) { t.Run("4-byte int", func(t *testing.T) {
@ -67,7 +68,7 @@ func TestEmitInt(t *testing.T) {
result := buf.Bytes() result := buf.Bytes()
assert.Equal(t, 5, len(result)) assert.Equal(t, 5, len(result))
assert.EqualValues(t, opcode.PUSHINT32, result[0]) assert.EqualValues(t, opcode.PUSHINT32, result[0])
assert.EqualValues(t, 1<<28, BytesToInt(result[1:]).Int64()) assert.EqualValues(t, 1<<28, bigint.FromBytes(result[1:]).Int64())
}) })
t.Run("negative 3-byte int with padding", func(t *testing.T) { t.Run("negative 3-byte int with padding", func(t *testing.T) {
@ -77,7 +78,7 @@ func TestEmitInt(t *testing.T) {
result := buf.Bytes() result := buf.Bytes()
assert.Equal(t, 5, len(result)) assert.Equal(t, 5, len(result))
assert.EqualValues(t, opcode.PUSHINT32, result[0]) assert.EqualValues(t, opcode.PUSHINT32, result[0])
assert.EqualValues(t, num, BytesToInt(result[1:]).Int64()) assert.EqualValues(t, num, bigint.FromBytes(result[1:]).Int64())
}) })
} }

View file

@ -6,6 +6,7 @@ import (
"sort" "sort"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// InteropFunc allows to hook into the VM. // InteropFunc allows to hook into the VM.
@ -87,7 +88,7 @@ func runtimeNotify(vm *VM) error {
// RuntimeSerialize handles syscalls System.Runtime.Serialize and Neo.Runtime.Serialize. // RuntimeSerialize handles syscalls System.Runtime.Serialize and Neo.Runtime.Serialize.
func RuntimeSerialize(vm *VM) error { func RuntimeSerialize(vm *VM) error {
item := vm.Estack().Pop() item := vm.Estack().Pop()
data, err := SerializeItem(item.value) data, err := stackitem.SerializeItem(item.value)
if err != nil { if err != nil {
return err return err
} else if len(data) > MaxItemSize { } else if len(data) > MaxItemSize {
@ -103,7 +104,7 @@ func RuntimeSerialize(vm *VM) error {
func RuntimeDeserialize(vm *VM) error { func RuntimeDeserialize(vm *VM) error {
data := vm.Estack().Pop().Bytes() data := vm.Estack().Pop().Bytes()
item, err := DeserializeItem(data) item, err := stackitem.DeserializeItem(data)
if err != nil { if err != nil {
return err return err
} }
@ -124,7 +125,7 @@ func init() {
func EnumeratorCreate(v *VM) error { func EnumeratorCreate(v *VM) error {
data := v.Estack().Pop().Array() data := v.Estack().Pop().Array()
v.Estack().Push(&Element{ v.Estack().Push(&Element{
value: NewInteropItem(&arrayWrapper{ value: stackitem.NewInterop(&arrayWrapper{
index: -1, index: -1,
value: data, value: data,
}), }),
@ -136,7 +137,7 @@ func EnumeratorCreate(v *VM) error {
// EnumeratorNext handles syscall Neo.Enumerator.Next. // EnumeratorNext handles syscall Neo.Enumerator.Next.
func EnumeratorNext(v *VM) error { func EnumeratorNext(v *VM) error {
iop := v.Estack().Pop().Interop() iop := v.Estack().Pop().Interop()
arr := iop.value.(enumerator) arr := iop.Value().(enumerator)
v.Estack().PushVal(arr.Next()) v.Estack().PushVal(arr.Next())
return nil return nil
@ -145,7 +146,7 @@ func EnumeratorNext(v *VM) error {
// EnumeratorValue handles syscall Neo.Enumerator.Value. // EnumeratorValue handles syscall Neo.Enumerator.Value.
func EnumeratorValue(v *VM) error { func EnumeratorValue(v *VM) error {
iop := v.Estack().Pop().Interop() iop := v.Estack().Pop().Interop()
arr := iop.value.(enumerator) arr := iop.Value().(enumerator)
v.Estack().Push(&Element{value: arr.Value()}) v.Estack().Push(&Element{value: arr.Value()})
return nil return nil
@ -154,12 +155,12 @@ func EnumeratorValue(v *VM) error {
// EnumeratorConcat handles syscall Neo.Enumerator.Concat. // EnumeratorConcat handles syscall Neo.Enumerator.Concat.
func EnumeratorConcat(v *VM) error { func EnumeratorConcat(v *VM) error {
iop1 := v.Estack().Pop().Interop() iop1 := v.Estack().Pop().Interop()
arr1 := iop1.value.(enumerator) arr1 := iop1.Value().(enumerator)
iop2 := v.Estack().Pop().Interop() iop2 := v.Estack().Pop().Interop()
arr2 := iop2.value.(enumerator) arr2 := iop2.Value().(enumerator)
v.Estack().Push(&Element{ v.Estack().Push(&Element{
value: NewInteropItem(&concatEnum{ value: stackitem.NewInterop(&concatEnum{
current: arr1, current: arr1,
second: arr2, second: arr2,
}), }),
@ -171,14 +172,14 @@ func EnumeratorConcat(v *VM) error {
// IteratorCreate handles syscall Neo.Iterator.Create. // IteratorCreate handles syscall Neo.Iterator.Create.
func IteratorCreate(v *VM) error { func IteratorCreate(v *VM) error {
data := v.Estack().Pop() data := v.Estack().Pop()
var item StackItem var item stackitem.Item
switch t := data.value.(type) { switch t := data.value.(type) {
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
item = NewInteropItem(&arrayWrapper{ item = stackitem.NewInterop(&arrayWrapper{
index: -1, index: -1,
value: t.Value().([]StackItem), value: t.Value().([]stackitem.Item),
}) })
case *MapItem: case *stackitem.Map:
item = NewMapIterator(t) item = NewMapIterator(t)
default: default:
return errors.New("non-iterable type") return errors.New("non-iterable type")
@ -189,21 +190,21 @@ func IteratorCreate(v *VM) error {
} }
// NewMapIterator returns new interop item containing iterator over m. // NewMapIterator returns new interop item containing iterator over m.
func NewMapIterator(m *MapItem) *InteropItem { func NewMapIterator(m *stackitem.Map) *stackitem.Interop {
return NewInteropItem(&mapWrapper{ return stackitem.NewInterop(&mapWrapper{
index: -1, index: -1,
m: m.value, m: m.Value().([]stackitem.MapElement),
}) })
} }
// IteratorConcat handles syscall Neo.Iterator.Concat. // IteratorConcat handles syscall Neo.Iterator.Concat.
func IteratorConcat(v *VM) error { func IteratorConcat(v *VM) error {
iop1 := v.Estack().Pop().Interop() iop1 := v.Estack().Pop().Interop()
iter1 := iop1.value.(iterator) iter1 := iop1.Value().(iterator)
iop2 := v.Estack().Pop().Interop() iop2 := v.Estack().Pop().Interop()
iter2 := iop2.value.(iterator) iter2 := iop2.Value().(iterator)
v.Estack().Push(&Element{value: NewInteropItem( v.Estack().Push(&Element{value: stackitem.NewInterop(
&concatIter{ &concatIter{
current: iter1, current: iter1,
second: iter2, second: iter2,
@ -216,7 +217,7 @@ func IteratorConcat(v *VM) error {
// IteratorKey handles syscall Neo.Iterator.Key. // IteratorKey handles syscall Neo.Iterator.Key.
func IteratorKey(v *VM) error { func IteratorKey(v *VM) error {
iop := v.estack.Pop().Interop() iop := v.estack.Pop().Interop()
iter := iop.value.(iterator) iter := iop.Value().(iterator)
v.Estack().Push(&Element{value: iter.Key()}) v.Estack().Push(&Element{value: iter.Key()})
return nil return nil
@ -225,8 +226,8 @@ func IteratorKey(v *VM) error {
// IteratorKeys handles syscall Neo.Iterator.Keys. // IteratorKeys handles syscall Neo.Iterator.Keys.
func IteratorKeys(v *VM) error { func IteratorKeys(v *VM) error {
iop := v.estack.Pop().Interop() iop := v.estack.Pop().Interop()
iter := iop.value.(iterator) iter := iop.Value().(iterator)
v.Estack().Push(&Element{value: NewInteropItem( v.Estack().Push(&Element{value: stackitem.NewInterop(
&keysWrapper{iter}, &keysWrapper{iter},
)}) )})
@ -236,8 +237,8 @@ func IteratorKeys(v *VM) error {
// IteratorValues handles syscall Neo.Iterator.Values. // IteratorValues handles syscall Neo.Iterator.Values.
func IteratorValues(v *VM) error { func IteratorValues(v *VM) error {
iop := v.estack.Pop().Interop() iop := v.estack.Pop().Interop()
iter := iop.value.(iterator) iter := iop.Value().(iterator)
v.Estack().Push(&Element{value: NewInteropItem( v.Estack().Push(&Element{value: stackitem.NewInterop(
&valuesWrapper{iter}, &valuesWrapper{iter},
)}) )})

View file

@ -1,14 +1,18 @@
package vm package vm
import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
type ( type (
enumerator interface { enumerator interface {
Next() bool Next() bool
Value() StackItem Value() stackitem.Item
} }
arrayWrapper struct { arrayWrapper struct {
index int index int
value []StackItem value []stackitem.Item
} }
concatEnum struct { concatEnum struct {
@ -20,12 +24,12 @@ type (
type ( type (
iterator interface { iterator interface {
enumerator enumerator
Key() StackItem Key() stackitem.Item
} }
mapWrapper struct { mapWrapper struct {
index int index int
m []MapElement m []stackitem.MapElement
} }
concatIter struct { concatIter struct {
@ -51,12 +55,12 @@ func (a *arrayWrapper) Next() bool {
return false return false
} }
func (a *arrayWrapper) Value() StackItem { func (a *arrayWrapper) Value() stackitem.Item {
return a.value[a.index] return a.value[a.index]
} }
func (a *arrayWrapper) Key() StackItem { func (a *arrayWrapper) Key() stackitem.Item {
return makeStackItem(a.index) return stackitem.Make(a.index)
} }
func (c *concatEnum) Next() bool { func (c *concatEnum) Next() bool {
@ -68,7 +72,7 @@ func (c *concatEnum) Next() bool {
return c.current.Next() return c.current.Next()
} }
func (c *concatEnum) Value() StackItem { func (c *concatEnum) Value() stackitem.Item {
return c.current.Value() return c.current.Value()
} }
@ -81,11 +85,11 @@ func (i *concatIter) Next() bool {
return i.second.Next() return i.second.Next()
} }
func (i *concatIter) Value() StackItem { func (i *concatIter) Value() stackitem.Item {
return i.current.Value() return i.current.Value()
} }
func (i *concatIter) Key() StackItem { func (i *concatIter) Key() stackitem.Item {
return i.current.Key() return i.current.Key()
} }
@ -98,11 +102,11 @@ func (m *mapWrapper) Next() bool {
return false return false
} }
func (m *mapWrapper) Value() StackItem { func (m *mapWrapper) Value() stackitem.Item {
return m.m[m.index].Value return m.m[m.index].Value
} }
func (m *mapWrapper) Key() StackItem { func (m *mapWrapper) Key() stackitem.Item {
return m.m[m.index].Key return m.m[m.index].Key
} }
@ -110,7 +114,7 @@ func (e *keysWrapper) Next() bool {
return e.iter.Next() return e.iter.Next()
} }
func (e *keysWrapper) Value() StackItem { func (e *keysWrapper) Value() stackitem.Item {
return e.iter.Key() return e.iter.Key()
} }
@ -118,6 +122,6 @@ func (e *valuesWrapper) Next() bool {
return e.iter.Next() return e.iter.Next()
} }
func (e *valuesWrapper) Value() StackItem { func (e *valuesWrapper) Value() stackitem.Item {
return e.iter.Value() return e.iter.Value()
} }

View file

@ -16,8 +16,9 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -112,7 +113,7 @@ func TestUT(t *testing.T) {
func getTestingInterop(id uint32) *InteropFuncPrice { func getTestingInterop(id uint32) *InteropFuncPrice {
if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) { if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) {
return &InteropFuncPrice{InteropFunc(func(v *VM) error { return &InteropFuncPrice{InteropFunc(func(v *VM) error {
v.estack.PushVal(&InteropItem{new(int)}) v.estack.PushVal(stackitem.NewInterop(new(int)))
return nil return nil
}), 0} }), 0}
} }
@ -174,17 +175,17 @@ func testFile(t *testing.T, filename string) {
}) })
} }
func compareItems(t *testing.T, a, b StackItem) { func compareItems(t *testing.T, a, b stackitem.Item) {
switch si := a.(type) { switch si := a.(type) {
case *BigIntegerItem: case *stackitem.BigInteger:
val := si.value.Int64() val := si.Value().(*big.Int).Int64()
switch ac := b.(type) { switch ac := b.(type) {
case *BigIntegerItem: case *stackitem.BigInteger:
require.Equal(t, val, ac.value.Int64()) require.Equal(t, val, ac.Value().(*big.Int).Int64())
case *ByteArrayItem: case *stackitem.ByteArray:
require.Equal(t, val, emit.BytesToInt(ac.value).Int64()) require.Equal(t, val, bigint.FromBytes(ac.Value().([]byte)).Int64())
case *BoolItem: case *stackitem.Bool:
if ac.value { if ac.Value().(bool) {
require.Equal(t, val, int64(1)) require.Equal(t, val, int64(1))
} else { } else {
require.Equal(t, val, int64(0)) require.Equal(t, val, int64(0))
@ -192,17 +193,17 @@ func compareItems(t *testing.T, a, b StackItem) {
default: default:
require.Fail(t, "wrong type") require.Fail(t, "wrong type")
} }
case *PointerItem: case *stackitem.Pointer:
p, ok := b.(*PointerItem) p, ok := b.(*stackitem.Pointer)
require.True(t, ok) require.True(t, ok)
require.Equal(t, si.pos, p.pos) // there no script in test files require.Equal(t, si.Position(), p.Position()) // there no script in test files
default: default:
require.Equal(t, a, b) require.Equal(t, a, b)
} }
} }
func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) { func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) {
compareItemArrays(t, expected, actual.Len(), func(i int) StackItem { return actual.Peek(i).Item() }) compareItemArrays(t, expected, actual.Len(), func(i int) stackitem.Item { return actual.Peek(i).Item() })
} }
func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) { func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
@ -213,7 +214,7 @@ func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
compareItemArrays(t, expected, actual.Size(), actual.Get) compareItemArrays(t, expected, actual.Size(), actual.Get)
} }
func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem func(i int) StackItem) { func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem func(i int) stackitem.Item) {
if expected == nil { if expected == nil {
return return
} }
@ -224,57 +225,47 @@ func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem fu
require.NotNil(t, it) require.NotNil(t, it)
if item.Type == typeInterop { if item.Type == typeInterop {
require.IsType(t, (*InteropItem)(nil), it) require.IsType(t, (*stackitem.Interop)(nil), it)
continue continue
} }
compareItems(t, item.toStackItem(), it) compareItems(t, item.toStackItem(), it)
} }
} }
func (v *vmUTStackItem) toStackItem() StackItem { func (v *vmUTStackItem) toStackItem() stackitem.Item {
switch v.Type.toLower() { switch v.Type.toLower() {
case typeArray: case typeArray:
items := v.Value.([]vmUTStackItem) items := v.Value.([]vmUTStackItem)
result := make([]StackItem, len(items)) result := make([]stackitem.Item, len(items))
for i := range items { for i := range items {
result[i] = items[i].toStackItem() result[i] = items[i].toStackItem()
} }
return &ArrayItem{ return stackitem.NewArray(result)
value: result,
}
case typeString: case typeString:
panic("not implemented") panic("not implemented")
case typeMap: case typeMap:
return v.Value.(*MapItem) return v.Value.(*stackitem.Map)
case typeInterop: case typeInterop:
panic("not implemented") panic("not implemented")
case typeByteString: case typeByteString:
return &ByteArrayItem{ return stackitem.NewByteArray(v.Value.([]byte))
v.Value.([]byte),
}
case typeBuffer: case typeBuffer:
return &BufferItem{v.Value.([]byte)} return stackitem.NewBuffer(v.Value.([]byte))
case typePointer: case typePointer:
return NewPointerItem(v.Value.(int), nil) return stackitem.NewPointer(v.Value.(int), nil)
case typeNull: case typeNull:
return NullItem{} return stackitem.Null{}
case typeBoolean: case typeBoolean:
return &BoolItem{ return stackitem.NewBool(v.Value.(bool))
v.Value.(bool),
}
case typeInteger: case typeInteger:
return &BigIntegerItem{ return stackitem.NewBigInteger(v.Value.(*big.Int))
value: v.Value.(*big.Int),
}
case typeStruct: case typeStruct:
items := v.Value.([]vmUTStackItem) items := v.Value.([]vmUTStackItem)
result := make([]StackItem, len(items)) result := make([]stackitem.Item, len(items))
for i := range items { for i := range items {
result[i] = items[i].toStackItem() result[i] = items[i].toStackItem()
} }
return &StructItem{ return stackitem.NewStruct(result)
value: result,
}
default: default:
panic(fmt.Sprintf("invalid type: %s", v.Type)) panic(fmt.Sprintf("invalid type: %s", v.Type))
} }
@ -303,10 +294,10 @@ func execStep(t *testing.T, v *VM, step vmUTStep) {
} }
} }
func jsonStringToInteger(s string) StackItem { func jsonStringToInteger(s string) stackitem.Item {
b, err := decodeHex(s) b, err := decodeHex(s)
if err == nil { if err == nil {
return NewBigIntegerItem(new(big.Int).SetBytes(b)) return stackitem.NewBigInteger(new(big.Int).SetBytes(b))
} }
return nil return nil
} }
@ -418,7 +409,7 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
return fmt.Errorf("invalid map start") return fmt.Errorf("invalid map start")
} }
result := NewMapItem() result := stackitem.NewMap()
for { for {
tok, err := d.Token() tok, err := d.Token()
if err != nil { if err != nil {
@ -438,7 +429,7 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
item := jsonStringToInteger(key) item := jsonStringToInteger(key)
if item == nil { if item == nil {
return fmt.Errorf("can't unmarshal StackItem %s", key) return fmt.Errorf("can't unmarshal Item %s", key)
} }
result.Add(item, it.toStackItem()) result.Add(item, it.toStackItem())
} }

View file

@ -1,46 +1,50 @@
package vm package vm
import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// refCounter represents reference counter for the VM. // refCounter represents reference counter for the VM.
type refCounter struct { type refCounter struct {
items map[StackItem]int items map[stackitem.Item]int
size int size int
} }
func newRefCounter() *refCounter { func newRefCounter() *refCounter {
return &refCounter{ return &refCounter{
items: make(map[StackItem]int), items: make(map[stackitem.Item]int),
} }
} }
// Add adds an item to the reference counter. // Add adds an item to the reference counter.
func (r *refCounter) Add(item StackItem) { func (r *refCounter) Add(item stackitem.Item) {
r.size++ r.size++
switch item.(type) { switch item.(type) {
case *ArrayItem, *StructItem, *MapItem: case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
if r.items[item]++; r.items[item] > 1 { if r.items[item]++; r.items[item] > 1 {
return return
} }
switch t := item.(type) { switch t := item.(type) {
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]StackItem) { for _, it := range item.Value().([]stackitem.Item) {
r.Add(it) r.Add(it)
} }
case *MapItem: case *stackitem.Map:
for i := range t.value { for i := range t.Value().([]stackitem.MapElement) {
r.Add(t.value[i].Value) r.Add(t.Value().([]stackitem.MapElement)[i].Value)
} }
} }
} }
} }
// Remove removes item from the reference counter. // Remove removes item from the reference counter.
func (r *refCounter) Remove(item StackItem) { func (r *refCounter) Remove(item stackitem.Item) {
r.size-- r.size--
switch item.(type) { switch item.(type) {
case *ArrayItem, *StructItem, *MapItem: case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
if r.items[item] > 1 { if r.items[item] > 1 {
r.items[item]-- r.items[item]--
return return
@ -49,13 +53,13 @@ func (r *refCounter) Remove(item StackItem) {
delete(r.items, item) delete(r.items, item)
switch t := item.(type) { switch t := item.(type) {
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]StackItem) { for _, it := range item.Value().([]stackitem.Item) {
r.Remove(it) r.Remove(it)
} }
case *MapItem: case *stackitem.Map:
for i := range t.value { for i := range t.Value().([]stackitem.MapElement) {
r.Remove(t.value[i].Value) r.Remove(t.Value().([]stackitem.MapElement)[i].Value)
} }
} }
} }

View file

@ -3,6 +3,7 @@ package vm
import ( import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -11,13 +12,13 @@ func TestRefCounter_Add(t *testing.T) {
require.Equal(t, 0, r.size) require.Equal(t, 0, r.size)
r.Add(NullItem{}) r.Add(stackitem.Null{})
require.Equal(t, 1, r.size) require.Equal(t, 1, r.size)
r.Add(NullItem{}) r.Add(stackitem.Null{})
require.Equal(t, 2, r.size) // count scalar items twice require.Equal(t, 2, r.size) // count scalar items twice
arr := NewArrayItem([]StackItem{NewByteArrayItem([]byte{1}), NewBoolItem(false)}) arr := stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1}), stackitem.NewBool(false)})
r.Add(arr) r.Add(arr)
require.Equal(t, 5, r.size) // array + 2 elements require.Equal(t, 5, r.size) // array + 2 elements

View file

@ -1,190 +0,0 @@
package vm
import (
"errors"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
// StackItemType represents type of the stack item.
type StackItemType byte
// This block defines all known stack item types.
const (
AnyT StackItemType = 0x00
PointerT StackItemType = 0x10
BooleanT StackItemType = 0x20
IntegerT StackItemType = 0x21
ByteArrayT StackItemType = 0x28
BufferT StackItemType = 0x30
ArrayT StackItemType = 0x40
StructT StackItemType = 0x41
MapT StackItemType = 0x48
InteropT StackItemType = 0x60
)
// String implements fmt.Stringer interface.
func (t StackItemType) String() string {
switch t {
case AnyT:
return "Any"
case PointerT:
return "Pointer"
case BooleanT:
return "Boolean"
case IntegerT:
return "Integer"
case ByteArrayT:
return "ByteArray"
case BufferT:
return "Buffer"
case ArrayT:
return "Array"
case StructT:
return "Struct"
case MapT:
return "Map"
case InteropT:
return "Interop"
default:
return "INVALID"
}
}
// IsValid checks if s is a well defined stack item type.
func (t StackItemType) IsValid() bool {
switch t {
case AnyT, PointerT, BooleanT, IntegerT, ByteArrayT, BufferT, ArrayT, StructT, MapT, InteropT:
return true
default:
return false
}
}
// SerializeItem encodes given StackItem into the byte slice.
func SerializeItem(item StackItem) ([]byte, error) {
w := io.NewBufBinWriter()
EncodeBinaryStackItem(item, w.BinWriter)
if w.Err != nil {
return nil, w.Err
}
return w.Bytes(), nil
}
// EncodeBinaryStackItem encodes given StackItem into the given BinWriter. It's
// similar to io.Serializable's EncodeBinary, but works with StackItem
// interface.
func EncodeBinaryStackItem(item StackItem, w *io.BinWriter) {
serializeItemTo(item, w, make(map[StackItem]bool))
}
func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
if seen[item] {
w.Err = errors.New("recursive structures can't be serialized")
return
}
switch t := item.(type) {
case *ByteArrayItem:
w.WriteBytes([]byte{byte(ByteArrayT)})
w.WriteVarBytes(t.value)
case *BufferItem:
w.WriteBytes([]byte{byte(BufferT)})
w.WriteVarBytes(t.value)
case *BoolItem:
w.WriteBytes([]byte{byte(BooleanT)})
w.WriteBool(t.value)
case *BigIntegerItem:
w.WriteBytes([]byte{byte(IntegerT)})
w.WriteVarBytes(emit.IntToBytes(t.value))
case *InteropItem:
w.Err = errors.New("interop item can't be serialized")
case *ArrayItem, *StructItem:
seen[item] = true
_, isArray := t.(*ArrayItem)
if isArray {
w.WriteBytes([]byte{byte(ArrayT)})
} else {
w.WriteBytes([]byte{byte(StructT)})
}
arr := t.Value().([]StackItem)
w.WriteVarUint(uint64(len(arr)))
for i := range arr {
serializeItemTo(arr[i], w, seen)
}
case *MapItem:
seen[item] = true
w.WriteBytes([]byte{byte(MapT)})
w.WriteVarUint(uint64(len(t.value)))
for i := range t.value {
serializeItemTo(t.value[i].Key, w, seen)
serializeItemTo(t.value[i].Value, w, seen)
}
}
}
// DeserializeItem decodes StackItem from the given byte slice.
func DeserializeItem(data []byte) (StackItem, error) {
r := io.NewBinReaderFromBuf(data)
item := DecodeBinaryStackItem(r)
if r.Err != nil {
return nil, r.Err
}
return item, nil
}
// DecodeBinaryStackItem decodes previously serialized StackItem from the given
// reader. It's similar to the io.Serializable's DecodeBinary(), but implemented
// as a function because StackItem itself is an interface. Caveat: always check
// reader's error value before using the returned StackItem.
func DecodeBinaryStackItem(r *io.BinReader) StackItem {
var t = r.ReadB()
if r.Err != nil {
return nil
}
switch StackItemType(t) {
case ByteArrayT:
data := r.ReadVarBytes()
return NewByteArrayItem(data)
case BooleanT:
var b = r.ReadBool()
return NewBoolItem(b)
case IntegerT:
data := r.ReadVarBytes()
num := emit.BytesToInt(data)
return &BigIntegerItem{
value: num,
}
case ArrayT, StructT:
size := int(r.ReadVarUint())
arr := make([]StackItem, size)
for i := 0; i < size; i++ {
arr[i] = DecodeBinaryStackItem(r)
}
if StackItemType(t) == ArrayT {
return &ArrayItem{value: arr}
}
return &StructItem{value: arr}
case MapT:
size := int(r.ReadVarUint())
m := NewMapItem()
for i := 0; i < size; i++ {
key := DecodeBinaryStackItem(r)
value := DecodeBinaryStackItem(r)
if r.Err != nil {
break
}
m.Add(key, value)
}
return m
default:
r.Err = errors.New("unknown type")
return nil
}
}

View file

@ -1,15 +1,19 @@
package vm package vm
import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Slot is a fixed-size slice of stack items. // Slot is a fixed-size slice of stack items.
type Slot struct { type Slot struct {
storage []StackItem storage []stackitem.Item
refs *refCounter refs *refCounter
} }
// newSlot returns new slot of n items. // newSlot returns new slot of n items.
func newSlot(n int, refs *refCounter) *Slot { func newSlot(n int, refs *refCounter) *Slot {
return &Slot{ return &Slot{
storage: make([]StackItem, n), storage: make([]stackitem.Item, n),
refs: refs, refs: refs,
} }
} }
@ -19,7 +23,7 @@ func (v *VM) newSlot(n int) *Slot {
} }
// Set sets i-th storage slot. // Set sets i-th storage slot.
func (s *Slot) Set(i int, item StackItem) { func (s *Slot) Set(i int, item stackitem.Item) {
if s.storage[i] == item { if s.storage[i] == item {
return return
} }
@ -32,11 +36,11 @@ func (s *Slot) Set(i int, item StackItem) {
} }
// Get returns item contained in i-th slot. // Get returns item contained in i-th slot.
func (s *Slot) Get(i int) StackItem { func (s *Slot) Get(i int) stackitem.Item {
if item := s.storage[i]; item != nil { if item := s.storage[i]; item != nil {
return item return item
} }
return NullItem{} return stackitem.Null{}
} }
// Size returns slot size. // Size returns slot size.

View file

@ -4,6 +4,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -12,10 +13,10 @@ func TestSlot_Get(t *testing.T) {
require.NotNil(t, s) require.NotNil(t, s)
require.Equal(t, 3, s.Size()) require.Equal(t, 3, s.Size())
// NullItem is the default // Null is the default
item := s.Get(2) item := s.Get(2)
require.Equal(t, NullItem{}, item) require.Equal(t, stackitem.Null{}, item)
s.Set(1, NewBigIntegerItem(big.NewInt(42))) s.Set(1, stackitem.NewBigInteger(big.NewInt(42)))
require.Equal(t, NewBigIntegerItem(big.NewInt(42)), s.Get(1)) require.Equal(t, stackitem.NewBigInteger(big.NewInt(42)), s.Get(1))
} }

View file

@ -7,6 +7,7 @@ import (
"math/big" "math/big"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// Stack implementation for the neo-go virtual machine. The stack implements // Stack implementation for the neo-go virtual machine. The stack implements
@ -29,9 +30,9 @@ import (
// [ 0 ] // [ 0 ]
// Element represents an element in the double linked list (the stack), // Element represents an element in the double linked list (the stack),
// which will hold the underlying StackItem. // which will hold the underlying stackitem.Item.
type Element struct { type Element struct {
value StackItem value stackitem.Item
next, prev *Element next, prev *Element
stack *Stack stack *Stack
} }
@ -40,7 +41,7 @@ type Element struct {
// to the corresponding type. // to the corresponding type.
func NewElement(v interface{}) *Element { func NewElement(v interface{}) *Element {
return &Element{ return &Element{
value: makeStackItem(v), value: stackitem.Make(v),
} }
} }
@ -60,12 +61,12 @@ func (e *Element) Prev() *Element {
return nil return nil
} }
// Item returns StackItem contained in the element. // Item returns Item contained in the element.
func (e *Element) Item() StackItem { func (e *Element) Item() stackitem.Item {
return e.value return e.value
} }
// Value returns value of the StackItem contained in the element. // Value returns value of the Item contained in the element.
func (e *Element) Value() interface{} { func (e *Element) Value() interface{} {
return e.value.Value() return e.value.Value()
} }
@ -98,12 +99,12 @@ func (e *Element) Bytes() []byte {
// Array attempts to get the underlying value of the element as an array of // Array attempts to get the underlying value of the element as an array of
// other items. Will panic if the item type is different which will be caught // other items. Will panic if the item type is different which will be caught
// by the VM. // by the VM.
func (e *Element) Array() []StackItem { func (e *Element) Array() []stackitem.Item {
switch t := e.value.(type) { switch t := e.value.(type) {
case *ArrayItem: case *stackitem.Array:
return t.value return t.Value().([]stackitem.Item)
case *StructItem: case *stackitem.Struct:
return t.value return t.Value().([]stackitem.Item)
default: default:
panic("element is not an array") panic("element is not an array")
} }
@ -111,9 +112,9 @@ func (e *Element) Array() []StackItem {
// Interop attempts to get the underlying value of the element // Interop attempts to get the underlying value of the element
// as an interop item. // as an interop item.
func (e *Element) Interop() *InteropItem { func (e *Element) Interop() *stackitem.Interop {
switch t := e.value.(type) { switch t := e.value.(type) {
case *InteropItem: case *stackitem.Interop:
return t return t
default: default:
panic("element is not an interop") panic("element is not an interop")
@ -190,7 +191,7 @@ func (s *Stack) Push(e *Element) {
} }
// PushVal pushes the given value on the stack. It will infer the // PushVal pushes the given value on the stack. It will infer the
// underlying StackItem to its corresponding type. // underlying Item to its corresponding type.
func (s *Stack) PushVal(v interface{}) { func (s *Stack) PushVal(v interface{}) {
s.Push(NewElement(v)) s.Push(NewElement(v))
} }
@ -369,7 +370,7 @@ func (s *Stack) PopSigElements() ([][]byte, error) {
return nil, fmt.Errorf("nothing on the stack") return nil, fmt.Errorf("nothing on the stack")
} }
switch item.value.(type) { switch item.value.(type) {
case *ArrayItem: case *stackitem.Array:
num = len(item.Array()) num = len(item.Array())
if num < 1 { if num < 1 {
return nil, fmt.Errorf("less than one element in the array") return nil, fmt.Errorf("less than one element in the array")
@ -400,8 +401,8 @@ func (s *Stack) ToContractParameters() []smartcontract.Parameter {
items := make([]smartcontract.Parameter, 0, s.Len()) items := make([]smartcontract.Parameter, 0, s.Len())
s.IterBack(func(e *Element) { s.IterBack(func(e *Element) {
// Each item is independent. // Each item is independent.
seen := make(map[StackItem]bool) seen := make(map[stackitem.Item]bool)
items = append(items, e.value.ToContractParameter(seen)) items = append(items, smartcontract.ParameterFromStackItem(e.value, seen))
}) })
return items return items
} }

File diff suppressed because it is too large Load diff

View file

@ -1,485 +0,0 @@
package vm
import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/stretchr/testify/assert"
)
var makeStackItemTestCases = []struct {
input interface{}
result StackItem
}{
{
input: int64(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: int16(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: 3,
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint8(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint16(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint32(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint64(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: big.NewInt(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: []byte{1, 2, 3, 4},
result: &ByteArrayItem{value: []byte{1, 2, 3, 4}},
},
{
input: []byte{},
result: &ByteArrayItem{value: []byte{}},
},
{
input: "bla",
result: &ByteArrayItem{value: []byte("bla")},
},
{
input: "",
result: &ByteArrayItem{value: []byte{}},
},
{
input: true,
result: &BoolItem{value: true},
},
{
input: false,
result: &BoolItem{value: false},
},
{
input: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}},
result: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}},
},
{
input: []int{1, 2, 3},
result: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(1)}, &BigIntegerItem{value: big.NewInt(2)}, &BigIntegerItem{value: big.NewInt(3)}}},
},
}
var makeStackItemErrorCases = []struct {
input interface{}
}{
{
input: nil,
},
}
func TestMakeStackItem(t *testing.T) {
for _, testCase := range makeStackItemTestCases {
assert.Equal(t, testCase.result, makeStackItem(testCase.input))
}
for _, errorCase := range makeStackItemErrorCases {
assert.Panics(t, func() { makeStackItem(errorCase.input) })
}
}
var stringerTestCases = []struct {
input StackItem
result string
}{
{
input: NewStructItem([]StackItem{}),
result: "Struct",
},
{
input: NewBigIntegerItem(big.NewInt(3)),
result: "BigInteger",
},
{
input: NewBoolItem(true),
result: "Boolean",
},
{
input: NewByteArrayItem([]byte{}),
result: "ByteArray",
},
{
input: NewArrayItem([]StackItem{}),
result: "Array",
},
{
input: NewMapItem(),
result: "Map",
},
{
input: NewInteropItem(nil),
result: "InteropItem",
},
{
input: NewPointerItem(0, nil),
result: "Pointer",
},
}
func TestStringer(t *testing.T) {
for _, testCase := range stringerTestCases {
assert.Equal(t, testCase.result, testCase.input.String())
}
}
var equalsTestCases = map[string][]struct {
item1 StackItem
item2 StackItem
result bool
}{
"struct": {
{
item1: NewStructItem(nil),
item2: nil,
result: false,
},
{
item1: NewStructItem(nil),
item2: NewBigIntegerItem(big.NewInt(1)),
result: false,
},
{
item1: NewStructItem(nil),
item2: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
result: false,
},
{
item1: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
item2: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(2))}),
result: false,
},
{
item1: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
item2: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
result: true,
},
},
"bigint": {
{
item1: NewBigIntegerItem(big.NewInt(2)),
item2: nil,
result: false,
},
{
item1: NewBigIntegerItem(big.NewInt(2)),
item2: NewBigIntegerItem(big.NewInt(2)),
result: true,
},
{
item1: NewBigIntegerItem(big.NewInt(2)),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBigIntegerItem(big.NewInt(0)),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBigIntegerItem(big.NewInt(2)),
item2: makeStackItem(int32(2)),
result: true,
},
},
"bool": {
{
item1: NewBoolItem(true),
item2: nil,
result: false,
},
{
item1: NewBoolItem(true),
item2: NewBoolItem(true),
result: true,
},
{
item1: NewBoolItem(true),
item2: NewBigIntegerItem(big.NewInt(1)),
result: true,
},
{
item1: NewBoolItem(true),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBoolItem(true),
item2: makeStackItem(true),
result: true,
},
},
"bytearray": {
{
item1: NewByteArrayItem(nil),
item2: nil,
result: false,
},
{
item1: NewByteArrayItem([]byte{1, 2, 3}),
item2: NewByteArrayItem([]byte{1, 2, 3}),
result: true,
},
{
item1: NewByteArrayItem([]byte{1}),
item2: NewBigIntegerItem(big.NewInt(1)),
result: true,
},
{
item1: NewByteArrayItem([]byte{1, 2, 3}),
item2: NewByteArrayItem([]byte{1, 2, 4}),
result: false,
},
{
item1: NewByteArrayItem([]byte{1, 2, 3}),
item2: makeStackItem([]byte{1, 2, 3}),
result: true,
},
},
"array": {
{
item1: NewArrayItem(nil),
item2: nil,
result: false,
},
{
item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}),
item2: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}),
result: false,
},
{
item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}}),
item2: NewBigIntegerItem(big.NewInt(1)),
result: false,
},
{
item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}),
item2: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(4)}}),
result: false,
},
},
"map": {
{
item1: NewMapItem(),
item2: nil,
result: false,
},
{
item1: NewMapItem(),
item2: NewMapItem(),
result: false,
},
{
item1: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(big.NewInt(1))}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(big.NewInt(1))}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
result: false,
},
{
item1: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(big.NewInt(1))}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(big.NewInt(1))}, {NewBoolItem(true), NewByteArrayItem([]byte{3})}}},
result: false,
},
},
"interop": {
{
item1: NewInteropItem(nil),
item2: nil,
result: false,
},
{
item1: NewInteropItem(nil),
item2: NewInteropItem(nil),
result: true,
},
{
item1: NewInteropItem(2),
item2: NewInteropItem(3),
result: false,
},
{
item1: NewInteropItem(3),
item2: NewInteropItem(3),
result: true,
},
},
"pointer": {
{
item1: NewPointerItem(0, []byte{}),
result: false,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(1, []byte{1}),
result: true,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(2, []byte{1}),
result: false,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(1, []byte{2}),
result: false,
},
{
item1: NewPointerItem(0, []byte{}),
item2: NewBigIntegerItem(big.NewInt(0)),
result: false,
},
},
}
func TestEquals(t *testing.T) {
for name, testBatch := range equalsTestCases {
for _, testCase := range testBatch {
t.Run(name, func(t *testing.T) {
assert.Equal(t, testCase.result, testCase.item1.Equals(testCase.item2))
// Reference equals
assert.Equal(t, true, testCase.item1.Equals(testCase.item1))
})
}
}
}
var marshalJSONTestCases = []struct {
input StackItem
result []byte
}{
{
input: NewBigIntegerItem(big.NewInt(2)),
result: []byte(`2`),
},
{
input: NewBoolItem(true),
result: []byte(`true`),
},
{
input: NewByteArrayItem([]byte{1, 2, 3}),
result: []byte(`"010203"`),
},
{
input: NewBufferItem([]byte{1, 2, 3}),
result: []byte(`"010203"`),
},
{
input: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}},
result: []byte(`[3,"010203"]`),
},
{
input: &InteropItem{value: 3},
result: []byte(`3`),
},
}
func TestMarshalJSON(t *testing.T) {
var (
actual []byte
err error
)
for _, testCase := range marshalJSONTestCases {
switch testCase.input.(type) {
case *BigIntegerItem:
actual, err = testCase.input.(*BigIntegerItem).MarshalJSON()
case *BoolItem:
actual, err = testCase.input.(*BoolItem).MarshalJSON()
case *ByteArrayItem:
actual, err = testCase.input.(*ByteArrayItem).MarshalJSON()
case *ArrayItem:
actual, err = testCase.input.(*ArrayItem).MarshalJSON()
case *InteropItem:
actual, err = testCase.input.(*InteropItem).MarshalJSON()
default:
continue
}
assert.NoError(t, err)
assert.Equal(t, testCase.result, actual)
}
}
var toContractParameterTestCases = []struct {
input StackItem
result smartcontract.Parameter
}{
{
input: NewStructItem([]StackItem{
NewBigIntegerItem(big.NewInt(1)),
NewBoolItem(true),
}),
result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{
{Type: smartcontract.IntegerType, Value: int64(1)},
{Type: smartcontract.BoolType, Value: true},
}},
},
{
input: NewBoolItem(false),
result: smartcontract.Parameter{Type: smartcontract.BoolType, Value: false},
},
{
input: NewByteArrayItem([]byte{0x01, 0x02, 0x03}),
result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
},
{
input: NewBufferItem([]byte{0x01, 0x02, 0x03}),
result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
},
{
input: NewArrayItem([]StackItem{NewBigIntegerItem(big.NewInt(2)), NewBoolItem(true)}),
result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{
{Type: smartcontract.IntegerType, Value: int64(2)},
{Type: smartcontract.BoolType, Value: true},
}},
},
{
input: NewInteropItem(nil),
result: smartcontract.Parameter{Type: smartcontract.InteropInterfaceType, Value: nil},
},
{
input: &MapItem{value: []MapElement{
{NewBigIntegerItem(big.NewInt(1)), NewBoolItem(true)},
{NewByteArrayItem([]byte("qwerty")), NewBigIntegerItem(big.NewInt(3))},
{NewBoolItem(true), NewBoolItem(false)},
}},
result: smartcontract.Parameter{
Type: smartcontract.MapType,
Value: []smartcontract.ParameterPair{
{
Key: smartcontract.Parameter{Type: smartcontract.IntegerType, Value: int64(1)},
Value: smartcontract.Parameter{Type: smartcontract.BoolType, Value: true},
}, {
Key: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte("qwerty")},
Value: smartcontract.Parameter{Type: smartcontract.IntegerType, Value: int64(3)},
}, {
Key: smartcontract.Parameter{Type: smartcontract.BoolType, Value: true},
Value: smartcontract.Parameter{Type: smartcontract.BoolType, Value: false},
},
},
},
},
}
func TestToContractParameter(t *testing.T) {
for _, tc := range toContractParameterTestCases {
seen := make(map[StackItem]bool)
res := tc.input.ToContractParameter(seen)
assert.Equal(t, res, tc.result)
}
}

View file

@ -4,6 +4,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -231,9 +232,9 @@ func TestPushVal(t *testing.T) {
assert.Equal(t, true, elem.Bool()) assert.Equal(t, true, elem.Bool())
// array // array
s.PushVal([]StackItem{&BoolItem{true}, &BoolItem{false}, &BoolItem{true}}) s.PushVal([]stackitem.Item{stackitem.NewBool(true), stackitem.NewBool(false), stackitem.NewBool(true)})
elem = s.Pop() elem = s.Pop()
assert.IsType(t, elem.value, &ArrayItem{}) assert.IsType(t, elem.value, &stackitem.Array{})
} }
func TestSwapElemValues(t *testing.T) { func TestSwapElemValues(t *testing.T) {
@ -320,17 +321,17 @@ func TestPopSigElements(t *testing.T) {
_, err := s.PopSigElements() _, err := s.PopSigElements()
assert.NotNil(t, err) assert.NotNil(t, err)
s.PushVal([]StackItem{}) s.PushVal([]stackitem.Item{})
_, err = s.PopSigElements() _, err = s.PopSigElements()
assert.NotNil(t, err) assert.NotNil(t, err)
s.PushVal([]StackItem{NewBoolItem(false)}) s.PushVal([]stackitem.Item{stackitem.NewBool(false)})
_, err = s.PopSigElements() _, err = s.PopSigElements()
assert.NotNil(t, err) assert.NotNil(t, err)
b1 := []byte("smth") b1 := []byte("smth")
b2 := []byte("strange") b2 := []byte("strange")
s.PushVal([]StackItem{NewByteArrayItem(b1), NewByteArrayItem(b2)}) s.PushVal([]stackitem.Item{stackitem.NewByteArray(b1), stackitem.NewByteArray(b2)})
z, err := s.PopSigElements() z, err := s.PopSigElements()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, z, [][]byte{b1, b2}) assert.Equal(t, z, [][]byte{b1, b2})

1010
pkg/vm/stackitem/item.go Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,414 @@
package stackitem
import (
"math/big"
"testing"
"github.com/stretchr/testify/assert"
)
var makeStackItemTestCases = []struct {
input interface{}
result Item
}{
{
input: int64(3),
result: &BigInteger{value: big.NewInt(3)},
},
{
input: int16(3),
result: &BigInteger{value: big.NewInt(3)},
},
{
input: 3,
result: &BigInteger{value: big.NewInt(3)},
},
{
input: uint8(3),
result: &BigInteger{value: big.NewInt(3)},
},
{
input: uint16(3),
result: &BigInteger{value: big.NewInt(3)},
},
{
input: uint32(3),
result: &BigInteger{value: big.NewInt(3)},
},
{
input: uint64(3),
result: &BigInteger{value: big.NewInt(3)},
},
{
input: big.NewInt(3),
result: &BigInteger{value: big.NewInt(3)},
},
{
input: []byte{1, 2, 3, 4},
result: &ByteArray{value: []byte{1, 2, 3, 4}},
},
{
input: []byte{},
result: &ByteArray{value: []byte{}},
},
{
input: "bla",
result: &ByteArray{value: []byte("bla")},
},
{
input: "",
result: &ByteArray{value: []byte{}},
},
{
input: true,
result: &Bool{value: true},
},
{
input: false,
result: &Bool{value: false},
},
{
input: []Item{&BigInteger{value: big.NewInt(3)}, &ByteArray{value: []byte{1, 2, 3}}},
result: &Array{value: []Item{&BigInteger{value: big.NewInt(3)}, &ByteArray{value: []byte{1, 2, 3}}}},
},
{
input: []int{1, 2, 3},
result: &Array{value: []Item{&BigInteger{value: big.NewInt(1)}, &BigInteger{value: big.NewInt(2)}, &BigInteger{value: big.NewInt(3)}}},
},
}
var makeStackItemErrorCases = []struct {
input interface{}
}{
{
input: nil,
},
}
func TestMakeStackItem(t *testing.T) {
for _, testCase := range makeStackItemTestCases {
assert.Equal(t, testCase.result, Make(testCase.input))
}
for _, errorCase := range makeStackItemErrorCases {
assert.Panics(t, func() { Make(errorCase.input) })
}
}
var stringerTestCases = []struct {
input Item
result string
}{
{
input: NewStruct([]Item{}),
result: "Struct",
},
{
input: NewBigInteger(big.NewInt(3)),
result: "BigInteger",
},
{
input: NewBool(true),
result: "Boolean",
},
{
input: NewByteArray([]byte{}),
result: "ByteArray",
},
{
input: NewArray([]Item{}),
result: "Array",
},
{
input: NewMap(),
result: "Map",
},
{
input: NewInterop(nil),
result: "Interop",
},
{
input: NewPointer(0, nil),
result: "Pointer",
},
}
func TestStringer(t *testing.T) {
for _, testCase := range stringerTestCases {
assert.Equal(t, testCase.result, testCase.input.String())
}
}
var equalsTestCases = map[string][]struct {
item1 Item
item2 Item
result bool
}{
"struct": {
{
item1: NewStruct(nil),
item2: nil,
result: false,
},
{
item1: NewStruct(nil),
item2: NewBigInteger(big.NewInt(1)),
result: false,
},
{
item1: NewStruct(nil),
item2: NewStruct([]Item{NewBigInteger(big.NewInt(1))}),
result: false,
},
{
item1: NewStruct([]Item{NewBigInteger(big.NewInt(1))}),
item2: NewStruct([]Item{NewBigInteger(big.NewInt(2))}),
result: false,
},
{
item1: NewStruct([]Item{NewBigInteger(big.NewInt(1))}),
item2: NewStruct([]Item{NewBigInteger(big.NewInt(1))}),
result: true,
},
},
"bigint": {
{
item1: NewBigInteger(big.NewInt(2)),
item2: nil,
result: false,
},
{
item1: NewBigInteger(big.NewInt(2)),
item2: NewBigInteger(big.NewInt(2)),
result: true,
},
{
item1: NewBigInteger(big.NewInt(2)),
item2: NewBool(false),
result: false,
},
{
item1: NewBigInteger(big.NewInt(0)),
item2: NewBool(false),
result: false,
},
{
item1: NewBigInteger(big.NewInt(2)),
item2: Make(int32(2)),
result: true,
},
},
"bool": {
{
item1: NewBool(true),
item2: nil,
result: false,
},
{
item1: NewBool(true),
item2: NewBool(true),
result: true,
},
{
item1: NewBool(true),
item2: NewBigInteger(big.NewInt(1)),
result: true,
},
{
item1: NewBool(true),
item2: NewBool(false),
result: false,
},
{
item1: NewBool(true),
item2: Make(true),
result: true,
},
},
"bytearray": {
{
item1: NewByteArray(nil),
item2: nil,
result: false,
},
{
item1: NewByteArray([]byte{1, 2, 3}),
item2: NewByteArray([]byte{1, 2, 3}),
result: true,
},
{
item1: NewByteArray([]byte{1}),
item2: NewBigInteger(big.NewInt(1)),
result: true,
},
{
item1: NewByteArray([]byte{1, 2, 3}),
item2: NewByteArray([]byte{1, 2, 4}),
result: false,
},
{
item1: NewByteArray([]byte{1, 2, 3}),
item2: Make([]byte{1, 2, 3}),
result: true,
},
},
"array": {
{
item1: NewArray(nil),
item2: nil,
result: false,
},
{
item1: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(3)}}),
item2: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(3)}}),
result: false,
},
{
item1: NewArray([]Item{&BigInteger{big.NewInt(1)}}),
item2: NewBigInteger(big.NewInt(1)),
result: false,
},
{
item1: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(3)}}),
item2: NewArray([]Item{&BigInteger{big.NewInt(1)}, &BigInteger{big.NewInt(2)}, &BigInteger{big.NewInt(4)}}),
result: false,
},
},
"map": {
{
item1: NewMap(),
item2: nil,
result: false,
},
{
item1: NewMap(),
item2: NewMap(),
result: false,
},
{
item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}},
item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}},
result: false,
},
{
item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}},
item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{3})}}},
result: false,
},
},
"interop": {
{
item1: NewInterop(nil),
item2: nil,
result: false,
},
{
item1: NewInterop(nil),
item2: NewInterop(nil),
result: true,
},
{
item1: NewInterop(2),
item2: NewInterop(3),
result: false,
},
{
item1: NewInterop(3),
item2: NewInterop(3),
result: true,
},
},
"pointer": {
{
item1: NewPointer(0, []byte{}),
result: false,
},
{
item1: NewPointer(1, []byte{1}),
item2: NewPointer(1, []byte{1}),
result: true,
},
{
item1: NewPointer(1, []byte{1}),
item2: NewPointer(2, []byte{1}),
result: false,
},
{
item1: NewPointer(1, []byte{1}),
item2: NewPointer(1, []byte{2}),
result: false,
},
{
item1: NewPointer(0, []byte{}),
item2: NewBigInteger(big.NewInt(0)),
result: false,
},
},
}
func TestEquals(t *testing.T) {
for name, testBatch := range equalsTestCases {
for _, testCase := range testBatch {
t.Run(name, func(t *testing.T) {
assert.Equal(t, testCase.result, testCase.item1.Equals(testCase.item2))
// Reference equals
assert.Equal(t, true, testCase.item1.Equals(testCase.item1))
})
}
}
}
var marshalJSONTestCases = []struct {
input Item
result []byte
}{
{
input: NewBigInteger(big.NewInt(2)),
result: []byte(`2`),
},
{
input: NewBool(true),
result: []byte(`true`),
},
{
input: NewByteArray([]byte{1, 2, 3}),
result: []byte(`"010203"`),
},
{
input: NewBuffer([]byte{1, 2, 3}),
result: []byte(`"010203"`),
},
{
input: &Array{value: []Item{&BigInteger{value: big.NewInt(3)}, &ByteArray{value: []byte{1, 2, 3}}}},
result: []byte(`[3,"010203"]`),
},
{
input: &Interop{value: 3},
result: []byte(`3`),
},
}
func TestMarshalJSON(t *testing.T) {
var (
actual []byte
err error
)
for _, testCase := range marshalJSONTestCases {
switch testCase.input.(type) {
case *BigInteger:
actual, err = testCase.input.(*BigInteger).MarshalJSON()
case *Bool:
actual, err = testCase.input.(*Bool).MarshalJSON()
case *ByteArray:
actual, err = testCase.input.(*ByteArray).MarshalJSON()
case *Array:
actual, err = testCase.input.(*Array).MarshalJSON()
case *Interop:
actual, err = testCase.input.(*Interop).MarshalJSON()
default:
continue
}
assert.NoError(t, err)
assert.Equal(t, testCase.result, actual)
}
}

View file

@ -0,0 +1,134 @@
package stackitem
import (
"errors"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// SerializeItem encodes given Item into the byte slice.
func SerializeItem(item Item) ([]byte, error) {
w := io.NewBufBinWriter()
EncodeBinaryStackItem(item, w.BinWriter)
if w.Err != nil {
return nil, w.Err
}
return w.Bytes(), nil
}
// EncodeBinaryStackItem encodes given Item into the given BinWriter. It's
// similar to io.Serializable's EncodeBinary, but works with Item
// interface.
func EncodeBinaryStackItem(item Item, w *io.BinWriter) {
serializeItemTo(item, w, make(map[Item]bool))
}
func serializeItemTo(item Item, w *io.BinWriter, seen map[Item]bool) {
if seen[item] {
w.Err = errors.New("recursive structures can't be serialized")
return
}
switch t := item.(type) {
case *ByteArray:
w.WriteBytes([]byte{byte(ByteArrayT)})
w.WriteVarBytes(t.Value().([]byte))
case *Buffer:
w.WriteBytes([]byte{byte(BufferT)})
w.WriteVarBytes(t.Value().([]byte))
case *Bool:
w.WriteBytes([]byte{byte(BooleanT)})
w.WriteBool(t.Value().(bool))
case *BigInteger:
w.WriteBytes([]byte{byte(IntegerT)})
w.WriteVarBytes(bigint.ToBytes(t.Value().(*big.Int)))
case *Interop:
w.Err = errors.New("interop item can't be serialized")
case *Array, *Struct:
seen[item] = true
_, isArray := t.(*Array)
if isArray {
w.WriteBytes([]byte{byte(ArrayT)})
} else {
w.WriteBytes([]byte{byte(StructT)})
}
arr := t.Value().([]Item)
w.WriteVarUint(uint64(len(arr)))
for i := range arr {
serializeItemTo(arr[i], w, seen)
}
case *Map:
seen[item] = true
w.WriteBytes([]byte{byte(MapT)})
w.WriteVarUint(uint64(len(t.Value().([]MapElement))))
for i := range t.Value().([]MapElement) {
serializeItemTo(t.Value().([]MapElement)[i].Key, w, seen)
serializeItemTo(t.Value().([]MapElement)[i].Value, w, seen)
}
}
}
// DeserializeItem decodes Item from the given byte slice.
func DeserializeItem(data []byte) (Item, error) {
r := io.NewBinReaderFromBuf(data)
item := DecodeBinaryStackItem(r)
if r.Err != nil {
return nil, r.Err
}
return item, nil
}
// DecodeBinaryStackItem decodes previously serialized Item from the given
// reader. It's similar to the io.Serializable's DecodeBinary(), but implemented
// as a function because Item itself is an interface. Caveat: always check
// reader's error value before using the returned Item.
func DecodeBinaryStackItem(r *io.BinReader) Item {
var t = Type(r.ReadB())
if r.Err != nil {
return nil
}
switch t {
case ByteArrayT:
data := r.ReadVarBytes()
return NewByteArray(data)
case BooleanT:
var b = r.ReadBool()
return NewBool(b)
case IntegerT:
data := r.ReadVarBytes()
num := bigint.FromBytes(data)
return NewBigInteger(num)
case ArrayT, StructT:
size := int(r.ReadVarUint())
arr := make([]Item, size)
for i := 0; i < size; i++ {
arr[i] = DecodeBinaryStackItem(r)
}
if t == ArrayT {
return NewArray(arr)
}
return NewStruct(arr)
case MapT:
size := int(r.ReadVarUint())
m := NewMap()
for i := 0; i < size; i++ {
key := DecodeBinaryStackItem(r)
value := DecodeBinaryStackItem(r)
if r.Err != nil {
break
}
m.Add(key, value)
}
return m
default:
r.Err = errors.New("unknown type")
return nil
}
}

56
pkg/vm/stackitem/type.go Normal file
View file

@ -0,0 +1,56 @@
package stackitem
// Type represents type of the stack item.
type Type byte
// This block defines all known stack item types.
const (
AnyT Type = 0x00
PointerT Type = 0x10
BooleanT Type = 0x20
IntegerT Type = 0x21
ByteArrayT Type = 0x28
BufferT Type = 0x30
ArrayT Type = 0x40
StructT Type = 0x41
MapT Type = 0x48
InteropT Type = 0x60
)
// String implements fmt.Stringer interface.
func (t Type) String() string {
switch t {
case AnyT:
return "Any"
case PointerT:
return "Pointer"
case BooleanT:
return "Boolean"
case IntegerT:
return "Integer"
case ByteArrayT:
return "ByteArray"
case BufferT:
return "Buffer"
case ArrayT:
return "Array"
case StructT:
return "Struct"
case MapT:
return "Map"
case InteropT:
return "Interop"
default:
return "INVALID"
}
}
// IsValid checks if s is a well defined stack item type.
func (t Type) IsValid() bool {
switch t {
case AnyT, PointerT, BooleanT, IntegerT, ByteArrayT, BufferT, ArrayT, StructT, MapT, InteropT:
return true
default:
return false
}
}

View file

@ -12,9 +12,10 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"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/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -52,14 +53,11 @@ const (
// MaxInvocationStackSize is the maximum size of an invocation stack. // MaxInvocationStackSize is the maximum size of an invocation stack.
MaxInvocationStackSize = 1024 MaxInvocationStackSize = 1024
// MaxBigIntegerSizeBits is the maximum size of BigInt item in bits.
MaxBigIntegerSizeBits = 32 * 8
// MaxStackSize is the maximum number of items allowed to be // MaxStackSize is the maximum number of items allowed to be
// on all stacks at once. // on all stacks at once.
MaxStackSize = 2 * 1024 MaxStackSize = 2 * 1024
maxSHLArg = MaxBigIntegerSizeBits maxSHLArg = stackitem.MaxBigIntegerSizeBits
) )
// VM represents the virtual machine. // VM represents the virtual machine.
@ -165,7 +163,7 @@ func (v *VM) GetPublicKeys() map[string]*keys.PublicKey {
} }
// LoadArgs loads in the arguments used in the Mian entry point. // LoadArgs loads in the arguments used in the Mian entry point.
func (v *VM) LoadArgs(method []byte, args []StackItem) { func (v *VM) LoadArgs(method []byte, args []stackitem.Item) {
if len(args) > 0 { if len(args) > 0 {
v.estack.PushVal(args) v.estack.PushVal(args)
} }
@ -513,7 +511,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
if op <= opcode.PUSHINT256 { if op <= opcode.PUSHINT256 {
v.estack.PushVal(emit.BytesToInt(parameter)) v.estack.PushVal(bigint.FromBytes(parameter))
return return
} }
@ -534,22 +532,22 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n < 0 || int(n) > len(ctx.prog) { if n < 0 || int(n) > len(ctx.prog) {
panic(fmt.Sprintf("invalid pointer offset (%d)", n)) panic(fmt.Sprintf("invalid pointer offset (%d)", n))
} }
ptr := NewPointerItem(int(n), ctx.prog) ptr := stackitem.NewPointer(int(n), ctx.prog)
v.estack.PushVal(ptr) v.estack.PushVal(ptr)
case opcode.PUSHNULL: case opcode.PUSHNULL:
v.estack.PushVal(NullItem{}) v.estack.PushVal(stackitem.Null{})
case opcode.ISNULL: case opcode.ISNULL:
res := v.estack.Pop().value.Equals(NullItem{}) res := v.estack.Pop().value.Equals(stackitem.Null{})
v.estack.PushVal(res) v.estack.PushVal(res)
case opcode.ISTYPE: case opcode.ISTYPE:
res := v.estack.Pop().Item() res := v.estack.Pop().Item()
v.estack.PushVal(res.Type() == StackItemType(parameter[0])) v.estack.PushVal(res.Type() == stackitem.Type(parameter[0]))
case opcode.CONVERT: case opcode.CONVERT:
typ := StackItemType(parameter[0]) typ := stackitem.Type(parameter[0])
item := v.estack.Pop().Item() item := v.estack.Pop().Item()
result, err := item.Convert(typ) result, err := item.Convert(typ)
if err != nil { if err != nil {
@ -637,7 +635,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n < 0 || n > MaxItemSize { if n < 0 || n > MaxItemSize {
panic("invalid size") panic("invalid size")
} }
v.estack.PushVal(NewBufferItem(make([]byte, n))) v.estack.PushVal(stackitem.NewBuffer(make([]byte, n)))
case opcode.MEMCPY: case opcode.MEMCPY:
n := toInt(v.estack.Pop().BigInt()) n := toInt(v.estack.Pop().BigInt())
@ -656,7 +654,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if di < 0 { if di < 0 {
panic("invalid destination index") panic("invalid destination index")
} }
dst := v.estack.Pop().value.(*BufferItem).value dst := v.estack.Pop().value.(*stackitem.Buffer).Value().([]byte)
if sum := si + n; sum < 0 || sum > len(dst) { if sum := si + n; sum < 0 || sum > len(dst) {
panic("size is too big") panic("size is too big")
} }
@ -669,7 +667,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic(fmt.Sprintf("too big item: %d", l)) panic(fmt.Sprintf("too big item: %d", l))
} }
ab := append(a, b...) ab := append(a, b...)
v.estack.PushVal(NewBufferItem(ab)) v.estack.PushVal(stackitem.NewBuffer(ab))
case opcode.SUBSTR: case opcode.SUBSTR:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -685,7 +683,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if last > len(s) { if last > len(s) {
panic("invalid offset") panic("invalid offset")
} }
v.estack.PushVal(NewBufferItem(s[o:last])) v.estack.PushVal(stackitem.NewBuffer(s[o:last]))
case opcode.LEFT: case opcode.LEFT:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -696,7 +694,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if t := len(s); l > t { if t := len(s); l > t {
panic("size is too big") panic("size is too big")
} }
v.estack.PushVal(NewBufferItem(s[:l])) v.estack.PushVal(stackitem.NewBuffer(s[:l]))
case opcode.RIGHT: case opcode.RIGHT:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -704,7 +702,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("negative length") panic("negative length")
} }
s := v.estack.Pop().Bytes() s := v.estack.Pop().Bytes()
v.estack.PushVal(NewBufferItem(s[len(s)-l:])) v.estack.PushVal(stackitem.NewBuffer(s[len(s)-l:]))
case opcode.DEPTH: case opcode.DEPTH:
v.estack.PushVal(v.estack.Len()) v.estack.PushVal(v.estack.Len())
@ -801,7 +799,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// inplace // inplace
e := v.estack.Peek(0) e := v.estack.Peek(0)
i := e.BigInt() i := e.BigInt()
e.value = makeStackItem(i.Not(i)) e.value = stackitem.Make(i.Not(i))
case opcode.AND: case opcode.AND:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
@ -977,7 +975,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// Object operations // Object operations
case opcode.NEWARRAY0: case opcode.NEWARRAY0:
v.estack.PushVal(&ArrayItem{[]StackItem{}}) v.estack.PushVal(stackitem.NewArray([]stackitem.Item{}))
case opcode.NEWARRAY, opcode.NEWARRAYT: case opcode.NEWARRAY, opcode.NEWARRAYT:
item := v.estack.Pop() item := v.estack.Pop()
@ -985,15 +983,15 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n > MaxArraySize { if n > MaxArraySize {
panic("too long array") panic("too long array")
} }
typ := AnyT typ := stackitem.AnyT
if op == opcode.NEWARRAYT { if op == opcode.NEWARRAYT {
typ = StackItemType(parameter[0]) typ = stackitem.Type(parameter[0])
} }
items := makeArrayOfType(int(n), typ) items := makeArrayOfType(int(n), typ)
v.estack.PushVal(&ArrayItem{items}) v.estack.PushVal(stackitem.NewArray(items))
case opcode.NEWSTRUCT0: case opcode.NEWSTRUCT0:
v.estack.PushVal(&StructItem{[]StackItem{}}) v.estack.PushVal(stackitem.NewStruct([]stackitem.Item{}))
case opcode.NEWSTRUCT: case opcode.NEWSTRUCT:
item := v.estack.Pop() item := v.estack.Pop()
@ -1001,8 +999,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n > MaxArraySize { if n > MaxArraySize {
panic("too long struct") panic("too long struct")
} }
items := makeArrayOfType(int(n), AnyT) items := makeArrayOfType(int(n), stackitem.AnyT)
v.estack.PushVal(&StructItem{items}) v.estack.PushVal(stackitem.NewStruct(items))
case opcode.APPEND: case opcode.APPEND:
itemElem := v.estack.Pop() itemElem := v.estack.Pop()
@ -1011,20 +1009,16 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
val := cloneIfStruct(itemElem.value) val := cloneIfStruct(itemElem.value)
switch t := arrElem.value.(type) { switch t := arrElem.value.(type) {
case *ArrayItem: case *stackitem.Array:
arr := t.Value().([]StackItem) if t.Len() >= MaxArraySize {
if len(arr) >= MaxArraySize {
panic("too long array") panic("too long array")
} }
arr = append(arr, val) t.Append(val)
t.value = arr case *stackitem.Struct:
case *StructItem: if t.Len() >= MaxArraySize {
arr := t.Value().([]StackItem)
if len(arr) >= MaxArraySize {
panic("too long struct") panic("too long struct")
} }
arr = append(arr, val) t.Append(val)
t.value = arr
default: default:
panic("APPEND: not of underlying type Array") panic("APPEND: not of underlying type Array")
} }
@ -1037,7 +1031,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("OPACK: invalid length") panic("OPACK: invalid length")
} }
items := make([]StackItem, n) items := make([]stackitem.Item, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
items[i] = v.estack.Pop().value items[i] = v.estack.Pop().value
} }
@ -1060,20 +1054,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
index := int(key.BigInt().Int64()) index := int(key.BigInt().Int64())
switch t := obj.value.(type) { switch t := obj.value.(type) {
// Struct and Array items have their underlying value as []StackItem. // Struct and Array items have their underlying value as []Item.
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
arr := t.Value().([]StackItem) arr := t.Value().([]stackitem.Item)
if index < 0 || index >= len(arr) { if index < 0 || index >= len(arr) {
panic("PICKITEM: invalid index") panic("PICKITEM: invalid index")
} }
item := arr[index].Dup() item := arr[index].Dup()
v.estack.PushVal(item) v.estack.PushVal(item)
case *MapItem: case *stackitem.Map:
index := t.Index(key.Item()) index := t.Index(key.Item())
if index < 0 { if index < 0 {
panic("invalid key") panic("invalid key")
} }
v.estack.Push(&Element{value: t.value[index].Value.Dup()}) v.estack.Push(&Element{value: t.Value().([]stackitem.MapElement)[index].Value.Dup()})
default: default:
arr := obj.Bytes() arr := obj.Bytes()
if index < 0 || index >= len(arr) { if index < 0 || index >= len(arr) {
@ -1091,9 +1085,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
obj := v.estack.Pop() obj := v.estack.Pop()
switch t := obj.value.(type) { switch t := obj.value.(type) {
// Struct and Array items have their underlying value as []StackItem. // Struct and Array items have their underlying value as []Item.
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
arr := t.Value().([]StackItem) arr := t.Value().([]stackitem.Item)
index := int(key.BigInt().Int64()) index := int(key.BigInt().Int64())
if index < 0 || index >= len(arr) { if index < 0 || index >= len(arr) {
panic("SETITEM: invalid index") panic("SETITEM: invalid index")
@ -1101,18 +1095,18 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.refs.Remove(arr[index]) v.refs.Remove(arr[index])
arr[index] = item arr[index] = item
v.refs.Add(arr[index]) v.refs.Add(arr[index])
case *MapItem: case *stackitem.Map:
if i := t.Index(key.value); i >= 0 { if i := t.Index(key.value); i >= 0 {
v.refs.Remove(t.value[i].Value) v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value)
} else if len(t.value) >= MaxArraySize { } else if t.Len() >= MaxArraySize {
panic("too big map") panic("too big map")
} }
t.Add(key.value, item) t.Add(key.value, item)
v.refs.Add(item) v.refs.Add(item)
case *BufferItem: case *stackitem.Buffer:
index := toInt(key.BigInt()) index := toInt(key.BigInt())
if index < 0 || index >= len(t.value) { if index < 0 || index >= t.Len() {
panic("invalid index") panic("invalid index")
} }
bi, err := item.TryInteger() bi, err := item.TryInteger()
@ -1120,7 +1114,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if err != nil || b < math.MinInt8 || b > math.MaxUint8 { if err != nil || b < math.MinInt8 || b > math.MaxUint8 {
panic("invalid value") panic("invalid value")
} }
t.value[index] = byte(b) t.Value().([]byte)[index] = byte(b)
default: default:
panic(fmt.Sprintf("SETITEM: invalid item type %s", t)) panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
@ -1129,14 +1123,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.REVERSEITEMS: case opcode.REVERSEITEMS:
item := v.estack.Pop() item := v.estack.Pop()
switch t := item.value.(type) { switch t := item.value.(type) {
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
a := t.Value().([]StackItem) a := t.Value().([]stackitem.Item)
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i] a[i], a[j] = a[j], a[i]
} }
case *BufferItem: case *stackitem.Buffer:
for i, j := 0, len(t.value)-1; i < j; i, j = i+1, j-1 { for i, j := 0, t.Len()-1; i < j; i, j = i+1, j-1 {
t.value[i], t.value[j] = t.value[j], t.value[i] t.Value().([]byte)[i], t.Value().([]byte)[j] = t.Value().([]byte)[j], t.Value().([]byte)[i]
} }
default: default:
panic(fmt.Sprintf("invalid item type %s", t)) panic(fmt.Sprintf("invalid item type %s", t))
@ -1147,29 +1141,27 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
elem := v.estack.Pop() elem := v.estack.Pop()
switch t := elem.value.(type) { switch t := elem.value.(type) {
case *ArrayItem: case *stackitem.Array:
a := t.value a := t.Value().([]stackitem.Item)
k := int(key.BigInt().Int64()) k := int(key.BigInt().Int64())
if k < 0 || k >= len(a) { if k < 0 || k >= len(a) {
panic("REMOVE: invalid index") panic("REMOVE: invalid index")
} }
v.refs.Remove(a[k]) v.refs.Remove(a[k])
a = append(a[:k], a[k+1:]...) t.Remove(k)
t.value = a case *stackitem.Struct:
case *StructItem: a := t.Value().([]stackitem.Item)
a := t.value
k := int(key.BigInt().Int64()) k := int(key.BigInt().Int64())
if k < 0 || k >= len(a) { if k < 0 || k >= len(a) {
panic("REMOVE: invalid index") panic("REMOVE: invalid index")
} }
v.refs.Remove(a[k]) v.refs.Remove(a[k])
a = append(a[:k], a[k+1:]...) t.Remove(k)
t.value = a case *stackitem.Map:
case *MapItem:
index := t.Index(key.Item()) index := t.Index(key.Item())
// NEO 2.0 doesn't error on missing key. // NEO 2.0 doesn't error on missing key.
if index >= 0 { if index >= 0 {
v.refs.Remove(t.value[index].Value) v.refs.Remove(t.Value().([]stackitem.MapElement)[index].Value)
t.Drop(index) t.Drop(index)
} }
default: default:
@ -1179,21 +1171,21 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.CLEARITEMS: case opcode.CLEARITEMS:
elem := v.estack.Pop() elem := v.estack.Pop()
switch t := elem.value.(type) { switch t := elem.value.(type) {
case *ArrayItem: case *stackitem.Array:
for _, item := range t.value { for _, item := range t.Value().([]stackitem.Item) {
v.refs.Remove(item) v.refs.Remove(item)
} }
t.value = t.value[:0] t.Clear()
case *StructItem: case *stackitem.Struct:
for _, item := range t.value { for _, item := range t.Value().([]stackitem.Item) {
v.refs.Remove(item) v.refs.Remove(item)
} }
t.value = t.value[:0] t.Clear()
case *MapItem: case *stackitem.Map:
for i := range t.value { for i := range t.Value().([]stackitem.MapElement) {
v.refs.Remove(t.value[i].Value) v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value)
} }
t.value = t.value[:0] t.Clear()
default: default:
panic("CLEARITEMS: invalid type") panic("CLEARITEMS: invalid type")
} }
@ -1203,9 +1195,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// Cause there is no native (byte) item type here, hence we need to check // Cause there is no native (byte) item type here, hence we need to check
// the type of the item for array size operations. // the type of the item for array size operations.
switch t := elem.Value().(type) { switch t := elem.Value().(type) {
case []StackItem: case []stackitem.Item:
v.estack.PushVal(len(t)) v.estack.PushVal(len(t))
case []MapElement: case []stackitem.MapElement:
v.estack.PushVal(len(t)) v.estack.PushVal(len(t))
default: default:
v.estack.PushVal(len(elem.Bytes())) v.estack.PushVal(len(elem.Bytes()))
@ -1241,8 +1233,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.jumpIf(newCtx, offset, true) v.jumpIf(newCtx, offset, true)
case opcode.CALLA: case opcode.CALLA:
ptr := v.estack.Pop().Item().(*PointerItem) ptr := v.estack.Pop().Item().(*stackitem.Pointer)
if ptr.hash != ctx.ScriptHash() { if ptr.ScriptHash() != ctx.ScriptHash() {
panic("invalid script in pointer") panic("invalid script in pointer")
} }
@ -1251,7 +1243,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
newCtx.arguments = nil newCtx.arguments = nil
newCtx.rvcount = -1 newCtx.rvcount = -1
v.istack.PushVal(newCtx) v.istack.PushVal(newCtx)
v.jumpIf(newCtx, ptr.pos, true) v.jumpIf(newCtx, ptr.Position(), true)
case opcode.SYSCALL: case opcode.SYSCALL:
interopID := GetInteropID(parameter) interopID := GetInteropID(parameter)
@ -1291,7 +1283,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
case opcode.NEWMAP: case opcode.NEWMAP:
v.estack.Push(&Element{value: NewMapItem()}) v.estack.Push(&Element{value: stackitem.NewMap()})
case opcode.KEYS: case opcode.KEYS:
item := v.estack.Pop() item := v.estack.Pop()
@ -1299,14 +1291,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("no argument") panic("no argument")
} }
m, ok := item.value.(*MapItem) m, ok := item.value.(*stackitem.Map)
if !ok { if !ok {
panic("not a Map") panic("not a Map")
} }
arr := make([]StackItem, 0, len(m.value)) arr := make([]stackitem.Item, 0, m.Len())
for k := range m.value { for k := range m.Value().([]stackitem.MapElement) {
arr = append(arr, m.value[k].Key.Dup()) arr = append(arr, m.Value().([]stackitem.MapElement)[k].Key.Dup())
} }
v.estack.PushVal(arr) v.estack.PushVal(arr)
@ -1316,18 +1308,18 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("no argument") panic("no argument")
} }
var arr []StackItem var arr []stackitem.Item
switch t := item.value.(type) { switch t := item.value.(type) {
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
src := t.Value().([]StackItem) src := t.Value().([]stackitem.Item)
arr = make([]StackItem, len(src)) arr = make([]stackitem.Item, len(src))
for i := range src { for i := range src {
arr[i] = cloneIfStruct(src[i]) arr[i] = cloneIfStruct(src[i])
} }
case *MapItem: case *stackitem.Map:
arr = make([]StackItem, 0, len(t.value)) arr = make([]stackitem.Item, 0, t.Len())
for k := range t.value { for k := range t.Value().([]stackitem.MapElement) {
arr = append(arr, cloneIfStruct(t.value[k].Value)) arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value))
} }
default: default:
panic("not a Map, Array or Struct") panic("not a Map, Array or Struct")
@ -1344,20 +1336,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("no value found") panic("no value found")
} }
switch t := c.value.(type) { switch t := c.value.(type) {
case *ArrayItem, *StructItem: case *stackitem.Array, *stackitem.Struct:
index := key.BigInt().Int64() index := key.BigInt().Int64()
if index < 0 { if index < 0 {
panic("negative index") panic("negative index")
} }
v.estack.PushVal(index < int64(len(c.Array()))) v.estack.PushVal(index < int64(len(c.Array())))
case *MapItem: case *stackitem.Map:
v.estack.PushVal(t.Has(key.Item())) v.estack.PushVal(t.Has(key.Item()))
case *BufferItem: case *stackitem.Buffer:
index := key.BigInt().Int64() index := key.BigInt().Int64()
if index < 0 { if index < 0 {
panic("negative index") panic("negative index")
} }
v.estack.PushVal(index < int64(len(t.value))) v.estack.PushVal(index < int64(t.Len()))
default: default:
panic("wrong collection type") panic("wrong collection type")
} }
@ -1536,30 +1528,30 @@ func checkMultisig1(v *VM, h []byte, pkeys [][]byte, sig []byte) bool {
return false return false
} }
func cloneIfStruct(item StackItem) StackItem { func cloneIfStruct(item stackitem.Item) stackitem.Item {
switch it := item.(type) { switch it := item.(type) {
case *StructItem: case *stackitem.Struct:
return it.Clone() return it.Clone()
default: default:
return it return it
} }
} }
func makeArrayOfType(n int, typ StackItemType) []StackItem { func makeArrayOfType(n int, typ stackitem.Type) []stackitem.Item {
if !typ.IsValid() { if !typ.IsValid() {
panic(fmt.Sprintf("invalid stack item type: %d", typ)) panic(fmt.Sprintf("invalid stack item type: %d", typ))
} }
items := make([]StackItem, n) items := make([]stackitem.Item, n)
for i := range items { for i := range items {
switch typ { switch typ {
case BooleanT: case stackitem.BooleanT:
items[i] = NewBoolItem(false) items[i] = stackitem.NewBool(false)
case IntegerT: case stackitem.IntegerT:
items[i] = NewBigIntegerItem(big.NewInt(0)) items[i] = stackitem.NewBigInteger(big.NewInt(0))
case ByteArrayT: case stackitem.ByteArrayT:
items[i] = NewByteArrayItem([]byte{}) items[i] = stackitem.NewByteArray([]byte{})
default: default:
items[i] = NullItem{} items[i] = stackitem.Null{}
} }
} }
return items return items
@ -1569,7 +1561,7 @@ func validateMapKey(key *Element) {
if key == nil { if key == nil {
panic("no key found") panic("no key found")
} }
if !isValidMapKey(key.Item()) { if !stackitem.IsValidMapKey(key.Item()) {
panic("key can't be a collection") panic("key can't be a collection")
} }
} }

File diff suppressed because it is too large Load diff