vm: move StackItem to a separate package

closes #912
This commit is contained in:
Anna Shaleva 2020-06-03 15:55:06 +03:00
parent cd307c3cd0
commit 783f5ecb01
41 changed files with 2479 additions and 2418 deletions

View file

@ -17,6 +17,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"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.
// E.g. []byte("foobar") or []byte(scriptHash).
ast.Walk(c, n.Args[0])
c.emitConvert(vm.BufferT)
c.emitConvert(stackitem.BufferT)
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")
}
case "ToInteger", "ToByteArray", "ToBool":
typ := vm.IntegerT
typ := stackitem.IntegerT
switch name {
case "ToByteArray":
typ = vm.ByteArrayT
typ = stackitem.ByteArrayT
case "ToBool":
typ = vm.BooleanT
typ = stackitem.BooleanT
}
c.emitConvert(typ)
case "SHA256":
@ -1123,7 +1124,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
}
bytes := uint160.BytesBE()
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.
func (c *codegen) emitConvert(typ vm.StackItemType) {
func (c *codegen) emitConvert(typ stackitem.Type) {
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)
}
emit.Bytes(c.prog.BinWriter, buf)
c.emitConvert(vm.BufferT)
c.emitConvert(stackitem.BufferT)
}
func (c *codegen) convertMap(lit *ast.CompositeLit) {

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -41,8 +41,8 @@ func TestNotify(t *testing.T) {
require.NoError(t, v.Run())
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, []vm.StackItem{}, s.events[1].Value())
assert.Equal(t, []vm.StackItem{vm.NewByteArrayItem([]byte("single"))}, s.events[2].Value())
assert.Equal(t, []stackitem.Item{}, s.events[1].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/vm"
"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/require"
)
@ -38,7 +39,7 @@ func eval(t *testing.T, src string, result interface{}) {
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.LoadArgs(op, args)
err := vm.Run()
@ -73,7 +74,7 @@ func vmAndCompileInterop(t *testing.T, src string) (*vm.VM, *storagePlugin) {
type storagePlugin struct {
mem map[string][]byte
interops map[uint32]vm.InteropFunc
events []vm.StackItem
events []stackitem.Item
}
func newStoragePlugin() *storagePlugin {

View file

@ -23,6 +23,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"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/stackitem"
"github.com/pkg/errors"
"go.uber.org/zap"
)
@ -573,7 +574,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
return errors.Wrap(err, "failed to persist invocation results")
}
for _, note := range systemInterop.Notifications {
arr, ok := note.Item.Value().([]vm.StackItem)
arr, ok := note.Item.Value().([]stackitem.Item)
if !ok || len(arr) != 4 {
continue
}

View file

@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"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/stackitem"
"go.uber.org/zap"
)
@ -60,7 +61,7 @@ type Function struct {
}
// 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.
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/keys"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// ECDSAVerify checks ECDSA signature.
@ -48,12 +49,12 @@ func ECDSACheckMultisig(ic *interop.Context, v *vm.VM) error {
return nil
}
func getMessage(ic *interop.Context, item vm.StackItem) []byte {
func getMessage(ic *interop.Context, item stackitem.Item) []byte {
var msg []byte
switch val := item.(type) {
case *vm.InteropItem:
case *stackitem.Interop:
msg = val.Value().(crypto.Verifiable).GetSignedPart()
case vm.NullItem:
case stackitem.Null:
msg = ic.Container.GetSignedPart()
default:
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/vm"
"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/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
keyMap := make(map[string]*keys.PublicKey)
pkeys := make([]*keys.PrivateKey, n)
pubs := make([]vm.StackItem, n)
pubs := make([]stackitem.Item, n)
for i := range pubs {
pkeys[i], err = keys.NewPrivateKey()
if err != nil {
@ -27,25 +28,25 @@ func initCHECKMULTISIG(msg []byte, n int) ([]vm.StackItem, []vm.StackItem, map[s
pk := pkeys[i].PublicKey()
data := pk.Bytes()
pubs[i] = vm.NewByteArrayItem(data)
pubs[i] = stackitem.NewByteArray(data)
keyMap[string(data)] = pk
}
sigs := make([]vm.StackItem, n)
sigs := make([]stackitem.Item, n)
for i := range sigs {
sig := pkeys[i].Sign(msg)
sigs[i] = vm.NewByteArrayItem(sig)
sigs[i] = stackitem.NewByteArray(sig)
}
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 {
return arr
}
result := make([]vm.StackItem, len(indices))
result := make([]stackitem.Item, len(indices))
for i, j := range indices {
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/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
const (
@ -66,9 +67,9 @@ func txGetAttributes(ic *interop.Context, v *vm.VM) error {
if len(tx.Attributes) > vm.MaxArraySize {
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 {
attrs = append(attrs, vm.NewInteropItem(&tx.Attributes[i]))
attrs = append(attrs, stackitem.NewInterop(&tx.Attributes[i]))
}
v.Estack().PushVal(attrs)
return nil
@ -84,9 +85,9 @@ func txGetWitnesses(ic *interop.Context, v *vm.VM) error {
if len(tx.Scripts) > vm.MaxArraySize {
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 {
scripts = append(scripts, vm.NewInteropItem(&tx.Scripts[i]))
scripts = append(scripts, stackitem.NewInterop(&tx.Scripts[i]))
}
v.Estack().PushVal(scripts)
return nil
@ -139,7 +140,7 @@ func bcGetAccount(ic *interop.Context, v *vm.VM) error {
if err != nil {
return err
}
v.Estack().PushVal(vm.NewInteropItem(acc))
v.Estack().PushVal(stackitem.NewInterop(acc))
return nil
}
@ -204,13 +205,13 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
return err
}
filteredMap := vm.NewMapItem()
filteredMap := stackitem.NewMap()
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 {
return bytes.Compare(filteredMap.Value().([]vm.MapElement)[i].Key.Value().([]byte),
filteredMap.Value().([]vm.MapElement)[j].Key.Value().([]byte)) == -1
sort.Slice(filteredMap.Value().([]stackitem.MapElement), func(i, j int) bool {
return bytes.Compare(filteredMap.Value().([]stackitem.MapElement)[i].Key.Value().([]byte),
filteredMap.Value().([]stackitem.MapElement)[j].Key.Value().([]byte)) == -1
})
item := vm.NewMapIterator(filteredMap)
@ -288,7 +289,7 @@ func contractCreate(ic *interop.Context, v *vm.VM) error {
return err
}
}
v.Estack().PushVal(vm.NewInteropItem(contract))
v.Estack().PushVal(stackitem.NewInterop(contract))
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)
}

View file

@ -21,6 +21,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"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/stackitem"
"github.com/stretchr/testify/require"
)
@ -70,12 +71,12 @@ func TestStorageFind(t *testing.T) {
t.Run("normal invocation", func(t *testing.T) {
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)
require.NoError(t, err)
var iter *vm.InteropItem
var iter *stackitem.Interop
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
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) {
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)
require.NoError(t, err)
@ -119,7 +120,7 @@ func TestStorageFind(t *testing.T) {
t.Run("invalid type for StorageContext", func(t *testing.T) {
v.Estack().PushVal([]byte{0x01})
v.Estack().PushVal(vm.NewInteropItem(nil))
v.Estack().PushVal(stackitem.NewInterop(nil))
require.Error(t, storageFind(context, v))
})
@ -129,7 +130,7 @@ func TestStorageFind(t *testing.T) {
invalidHash[0] = ^invalidHash[0]
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))
})
@ -151,7 +152,7 @@ func TestHeaderGetVersion_Negative(t *testing.T) {
chain := newTestChain(t)
defer chain.Close()
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)
require.Errorf(t, err, "value is not a header or block")
@ -183,7 +184,7 @@ func TestTxGetAttributes(t *testing.T) {
err := txGetAttributes(context, v)
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)
}
@ -196,7 +197,7 @@ func TestWitnessGetVerificationScript(t *testing.T) {
defer chain.Close()
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)
require.NoError(t, err)
value := v.Estack().Pop().Value().([]byte)
@ -247,7 +248,7 @@ func TestECDSAVerify(t *testing.T) {
tx := transaction.New([]byte{0, 1, 2}, 1)
msg := tx.GetSignedPart()
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) {
@ -255,7 +256,7 @@ func TestECDSAVerify(t *testing.T) {
msg := tx.GetSignedPart()
sign := priv.Sign(msg)
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) {
@ -282,7 +283,7 @@ func TestECDSAVerify(t *testing.T) {
func TestAttrGetData(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))
err := attrGetData(context, v)
require.NoError(t, err)
@ -293,7 +294,7 @@ func TestAttrGetData(t *testing.T) {
func TestAttrGetUsage(t *testing.T) {
v, tx, context, chain := createVMAndTX(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))
err := attrGetUsage(context, v)
require.NoError(t, err)
@ -304,7 +305,7 @@ func TestAttrGetUsage(t *testing.T) {
func TestAccountGetScriptHash(t *testing.T) {
v, accState, context, chain := createVMAndAccState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(accState))
v.Estack().PushVal(stackitem.NewInterop(accState))
err := accountGetScriptHash(context, v)
require.NoError(t, err)
@ -315,7 +316,7 @@ func TestAccountGetScriptHash(t *testing.T) {
func TestContractGetScript(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(contractState))
v.Estack().PushVal(stackitem.NewInterop(contractState))
err := contractGetScript(context, v)
require.NoError(t, err)
@ -326,7 +327,7 @@ func TestContractGetScript(t *testing.T) {
func TestContractIsPayable(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(contractState))
v.Estack().PushVal(stackitem.NewInterop(contractState))
err := contractIsPayable(context, v)
require.NoError(t, err)
@ -341,13 +342,13 @@ func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context,
block := newDumbBlock()
chain := newTestChain(t)
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
}
func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
v, tx, context, chain := createVMAndTX(t)
v.Estack().PushVal(vm.NewInteropItem(tx))
v.Estack().PushVal(stackitem.NewInterop(tx))
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/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"go.uber.org/zap"
)
@ -58,7 +59,7 @@ func bcGetBlock(ic *interop.Context, v *vm.VM) error {
if err != nil {
v.Estack().PushVal([]byte{})
} else {
v.Estack().PushVal(vm.NewInteropItem(block))
v.Estack().PushVal(stackitem.NewInterop(block))
}
return nil
}
@ -74,7 +75,7 @@ func bcGetContract(ic *interop.Context, v *vm.VM) error {
if err != nil {
v.Estack().PushVal([]byte{})
} else {
v.Estack().PushVal(vm.NewInteropItem(cs))
v.Estack().PushVal(stackitem.NewInterop(cs))
}
return nil
}
@ -89,7 +90,7 @@ func bcGetHeader(ic *interop.Context, v *vm.VM) error {
if err != nil {
v.Estack().PushVal([]byte{})
} else {
v.Estack().PushVal(vm.NewInteropItem(header))
v.Estack().PushVal(stackitem.NewInterop(header))
}
return nil
}
@ -117,7 +118,7 @@ func bcGetTransaction(ic *interop.Context, v *vm.VM) error {
if err != nil {
return err
}
v.Estack().PushVal(vm.NewInteropItem(tx))
v.Estack().PushVal(stackitem.NewInterop(tx))
return nil
}
@ -208,9 +209,9 @@ func blockGetTransactions(ic *interop.Context, v *vm.VM) error {
if len(block.Transactions) > vm.MaxArraySize {
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 {
txes = append(txes, vm.NewInteropItem(tx))
txes = append(txes, stackitem.NewInterop(tx))
}
v.Estack().PushVal(txes)
return nil
@ -229,7 +230,7 @@ func blockGetTransaction(ic *interop.Context, v *vm.VM) error {
return errors.New("wrong transaction index")
}
tx := block.Transactions[index]
v.Estack().PushVal(vm.NewInteropItem(tx))
v.Estack().PushVal(stackitem.NewInterop(tx))
return nil
}
@ -247,7 +248,7 @@ func txGetHash(ic *interop.Context, v *vm.VM) error {
// engineGetScriptContainer returns transaction that contains the script being
// run.
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
}
@ -289,9 +290,9 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
// outside of the interop subsystem anyway. I'd probably fail transactions
// that emit such broken notifications, but that might break compatibility
// with testnet/mainnet, so we're replacing these with error messages.
_, err := vm.SerializeItem(item)
_, err := stackitem.SerializeItem(item)
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}
ic.Notifications = append(ic.Notifications, ne)
@ -387,7 +388,7 @@ func storageGetContext(ic *interop.Context, v *vm.VM) error {
ScriptHash: v.GetCurrentScriptHash(),
ReadOnly: false,
}
v.Estack().PushVal(vm.NewInteropItem(sc))
v.Estack().PushVal(stackitem.NewInterop(sc))
return nil
}
@ -397,7 +398,7 @@ func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
ScriptHash: v.GetCurrentScriptHash(),
ReadOnly: true,
}
v.Estack().PushVal(vm.NewInteropItem(sc))
v.Estack().PushVal(stackitem.NewInterop(sc))
return nil
}
@ -467,7 +468,7 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
}
stc = stx
}
v.Estack().PushVal(vm.NewInteropItem(stc))
v.Estack().PushVal(stackitem.NewInterop(stc))
return nil
}
@ -488,7 +489,7 @@ func contractCallEx(ic *interop.Context, v *vm.VM) error {
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)
if err != nil {
return errors.New("invalid contract hash")
@ -548,6 +549,6 @@ func contractGetStorageContext(ic *interop.Context, v *vm.VM) error {
stc := &StorageContext{
ScriptHash: cs.ScriptHash(),
}
v.Estack().PushVal(vm.NewInteropItem(stc))
v.Estack().PushVal(stackitem.NewInterop(stc))
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/manifest"
"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"
)
@ -194,7 +194,7 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
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])
end := uint32(toBigInt(args[1]).Int64())
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]
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]))
return vm.NewBoolItem(err == nil)
return stackitem.NewBool(err == nil)
}
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)
}
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])
arr := args[1].Value().([]vm.StackItem)
arr := args[1].Value().([]stackitem.Item)
var pubs keys.PublicKeys
for i := range arr {
pub := new(keys.PublicKey)
@ -241,7 +241,7 @@ func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
pubs = append(pubs, pub)
}
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.
@ -361,19 +361,19 @@ func (n *NEO) GetRegisteredValidators(d dao.DAO) ([]state.Validator, error) {
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)
if err != nil {
panic(err)
}
arr := make([]vm.StackItem, len(validators))
arr := make([]stackitem.Item, len(validators))
for i := range validators {
arr[i] = vm.NewStructItem([]vm.StackItem{
vm.NewByteArrayItem([]byte(validators[i].Key)),
vm.NewBigIntegerItem(validators[i].Votes),
arr[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte(validators[i].Key)),
stackitem.NewBigInteger(validators[i].Votes),
})
}
return vm.NewArrayItem(arr)
return stackitem.NewArray(arr)
}
// 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
}
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)
if err != nil {
panic(err)
@ -438,7 +438,7 @@ func (n *NEO) getValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem
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)
if err != nil {
panic(err)
@ -460,15 +460,15 @@ func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao
return pubs, nil
}
func pubsToArray(pubs keys.PublicKeys) vm.StackItem {
arr := make([]vm.StackItem, len(pubs))
func pubsToArray(pubs keys.PublicKeys) stackitem.Item {
arr := make([]stackitem.Item, len(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()
if err != nil {
panic(err)

View file

@ -10,8 +10,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"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/stackitem"
)
// prefixAccount is the standard prefix used to store account data.
@ -86,20 +86,20 @@ func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
return nil
}
func (c *nep5TokenNative) Name(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
return vm.NewByteArrayItem([]byte(c.name))
func (c *nep5TokenNative) Name(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
return stackitem.NewByteArray([]byte(c.name))
}
func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
return vm.NewByteArrayItem([]byte(c.symbol))
func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
return stackitem.NewByteArray([]byte(c.symbol))
}
func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
return vm.NewBigIntegerItem(big.NewInt(c.decimals))
func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
return stackitem.NewBigInteger(big.NewInt(c.decimals))
}
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
return vm.NewBigIntegerItem(c.getTotalSupply(ic))
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
return stackitem.NewBigInteger(c.getTotalSupply(ic))
}
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
@ -115,29 +115,29 @@ func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int)
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])
to := toUint160(args[1])
amount := toBigInt(args[2])
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 {
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) {
ne := state.NotificationEvent{
ScriptHash: c.Hash,
Item: vm.NewArrayItem([]vm.StackItem{
vm.NewByteArrayItem([]byte("Transfer")),
Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray([]byte("Transfer")),
addrToStackItem(from),
addrToStackItem(to),
vm.NewBigIntegerItem(amount),
stackitem.NewBigInteger(amount),
}),
}
ic.Notifications = append(ic.Notifications, ne)
@ -197,14 +197,14 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
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])
bs, err := ic.DAO.GetNEP5Balances(h)
if err != nil {
panic(err)
}
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) {
@ -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()
if err != nil {
panic(err)
@ -277,7 +277,7 @@ func toBigInt(s vm.StackItem) *big.Int {
return bi
}
func toUint160(s vm.StackItem) util.Uint160 {
func toUint160(s stackitem.Item) util.Uint160 {
buf, err := s.TryBytes()
if err != nil {
panic(err)

View file

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

View file

@ -5,14 +5,14 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"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.
type NotificationEvent struct {
ScriptHash util.Uint160
Item vm.StackItem
Item stackitem.Item
}
// AppExecResult represent the result of the script execution, gathering together
@ -29,13 +29,13 @@ type AppExecResult struct {
// EncodeBinary implements the Serializable interface.
func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) {
ne.ScriptHash.EncodeBinary(w)
vm.EncodeBinaryStackItem(ne.Item, w)
stackitem.EncodeBinaryStackItem(ne.Item, w)
}
// DecodeBinary implements the Serializable interface.
func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
ne.ScriptHash.DecodeBinary(r)
ne.Item = vm.DecodeBinaryStackItem(r)
ne.Item = stackitem.DecodeBinaryStackItem(r)
}
// 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/testserdes"
"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) {
event := &NotificationEvent{
ScriptHash: random.Uint160(),
Item: vm.NewBoolItem(true),
Item: stackitem.NewBool(true),
}
testserdes.EncodeDecodeBinary(t, event, new(NotificationEvent))

View file

@ -4,7 +4,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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
@ -33,8 +33,8 @@ type NotificationEvent struct {
// StateEventToResultNotification converts state.NotificationEvent to
// result.NotificationEvent.
func StateEventToResultNotification(event state.NotificationEvent) NotificationEvent {
seen := make(map[vm.StackItem]bool)
item := event.Item.ToContractParameter(seen)
seen := make(map[stackitem.Item]bool)
item := smartcontract.ParameterFromStackItem(event.Item, seen)
return NotificationEvent{
Contract: event.ScriptHash,
Item: item,

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

View file

@ -6,9 +6,9 @@ import (
"math/big"
"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/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Context represents the current execution context of the VM.
@ -170,50 +170,42 @@ func (c *Context) ScriptHash() util.Uint160 {
return c.scriptHash
}
// Value implements StackItem interface.
// Value implements stackitem.Item interface.
func (c *Context) Value() interface{} {
return c
}
// Dup implements StackItem interface.
func (c *Context) Dup() StackItem {
// Dup implements stackitem.Item interface.
func (c *Context) Dup() stackitem.Item {
return c
}
// Bool implements StackItem interface.
// Bool implements stackitem.Item interface.
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) {
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) {
return nil, errors.New("can't convert Context to Integer")
}
// Type implements StackItem interface.
func (c *Context) Type() StackItemType { panic("Context cannot appear on evaluation stack") }
// Type implements stackitem.Item interface.
func (c *Context) Type() stackitem.Type { panic("Context cannot appear on evaluation stack") }
// Convert implements StackItem interface.
func (c *Context) Convert(_ StackItemType) (StackItem, error) {
// Convert implements stackitem.Item interface.
func (c *Context) Convert(_ stackitem.Type) (stackitem.Item, error) {
panic("Context cannot be converted to anything")
}
// Equals implements StackItem interface.
func (c *Context) Equals(s StackItem) bool {
// Equals implements stackitem.Item interface.
func (c *Context) Equals(s stackitem.Item) bool {
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 {
for _, n := range c.breakPoints {
if n == c.ip {

View file

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

View file

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

View file

@ -18,6 +18,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
@ -112,7 +113,7 @@ func TestUT(t *testing.T) {
func getTestingInterop(id uint32) *InteropFuncPrice {
if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) {
return &InteropFuncPrice{InteropFunc(func(v *VM) error {
v.estack.PushVal(&InteropItem{new(int)})
v.estack.PushVal(stackitem.NewInterop(new(int)))
return nil
}), 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) {
case *BigIntegerItem:
val := si.value.Int64()
case *stackitem.BigInteger:
val := si.Value().(*big.Int).Int64()
switch ac := b.(type) {
case *BigIntegerItem:
require.Equal(t, val, ac.value.Int64())
case *ByteArrayItem:
require.Equal(t, val, emit.BytesToInt(ac.value).Int64())
case *BoolItem:
if ac.value {
case *stackitem.BigInteger:
require.Equal(t, val, ac.Value().(*big.Int).Int64())
case *stackitem.ByteArray:
require.Equal(t, val, emit.BytesToInt(ac.Value().([]byte)).Int64())
case *stackitem.Bool:
if ac.Value().(bool) {
require.Equal(t, val, int64(1))
} else {
require.Equal(t, val, int64(0))
@ -192,17 +193,17 @@ func compareItems(t *testing.T, a, b StackItem) {
default:
require.Fail(t, "wrong type")
}
case *PointerItem:
p, ok := b.(*PointerItem)
case *stackitem.Pointer:
p, ok := b.(*stackitem.Pointer)
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:
require.Equal(t, a, b)
}
}
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) {
@ -213,7 +214,7 @@ func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
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 {
return
}
@ -224,57 +225,47 @@ func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem fu
require.NotNil(t, it)
if item.Type == typeInterop {
require.IsType(t, (*InteropItem)(nil), it)
require.IsType(t, (*stackitem.Interop)(nil), it)
continue
}
compareItems(t, item.toStackItem(), it)
}
}
func (v *vmUTStackItem) toStackItem() StackItem {
func (v *vmUTStackItem) toStackItem() stackitem.Item {
switch v.Type.toLower() {
case typeArray:
items := v.Value.([]vmUTStackItem)
result := make([]StackItem, len(items))
result := make([]stackitem.Item, len(items))
for i := range items {
result[i] = items[i].toStackItem()
}
return &ArrayItem{
value: result,
}
return stackitem.NewArray(result)
case typeString:
panic("not implemented")
case typeMap:
return v.Value.(*MapItem)
return v.Value.(*stackitem.Map)
case typeInterop:
panic("not implemented")
case typeByteString:
return &ByteArrayItem{
v.Value.([]byte),
}
return stackitem.NewByteArray(v.Value.([]byte))
case typeBuffer:
return &BufferItem{v.Value.([]byte)}
return stackitem.NewBuffer(v.Value.([]byte))
case typePointer:
return NewPointerItem(v.Value.(int), nil)
return stackitem.NewPointer(v.Value.(int), nil)
case typeNull:
return NullItem{}
return stackitem.Null{}
case typeBoolean:
return &BoolItem{
v.Value.(bool),
}
return stackitem.NewBool(v.Value.(bool))
case typeInteger:
return &BigIntegerItem{
value: v.Value.(*big.Int),
}
return stackitem.NewBigInteger(v.Value.(*big.Int))
case typeStruct:
items := v.Value.([]vmUTStackItem)
result := make([]StackItem, len(items))
result := make([]stackitem.Item, len(items))
for i := range items {
result[i] = items[i].toStackItem()
}
return &StructItem{
value: result,
}
return stackitem.NewStruct(result)
default:
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)
if err == nil {
return NewBigIntegerItem(new(big.Int).SetBytes(b))
return stackitem.NewBigInteger(new(big.Int).SetBytes(b))
}
return nil
}
@ -418,7 +409,7 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
return fmt.Errorf("invalid map start")
}
result := NewMapItem()
result := stackitem.NewMap()
for {
tok, err := d.Token()
if err != nil {
@ -438,7 +429,7 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
item := jsonStringToInteger(key)
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())
}

View file

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

View file

@ -3,6 +3,7 @@ package vm
import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
@ -11,13 +12,13 @@ func TestRefCounter_Add(t *testing.T) {
require.Equal(t, 0, r.size)
r.Add(NullItem{})
r.Add(stackitem.Null{})
require.Equal(t, 1, r.size)
r.Add(NullItem{})
r.Add(stackitem.Null{})
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)
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
import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Slot is a fixed-size slice of stack items.
type Slot struct {
storage []StackItem
storage []stackitem.Item
refs *refCounter
}
// newSlot returns new slot of n items.
func newSlot(n int, refs *refCounter) *Slot {
return &Slot{
storage: make([]StackItem, n),
storage: make([]stackitem.Item, n),
refs: refs,
}
}
@ -19,7 +23,7 @@ func (v *VM) newSlot(n int) *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 {
return
}
@ -32,11 +36,11 @@ func (s *Slot) Set(i int, item StackItem) {
}
// 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 {
return item
}
return NullItem{}
return stackitem.Null{}
}
// Size returns slot size.

View file

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

View file

@ -7,6 +7,7 @@ import (
"math/big"
"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
@ -29,9 +30,9 @@ import (
// [ 0 ]
// 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 {
value StackItem
value stackitem.Item
next, prev *Element
stack *Stack
}
@ -40,7 +41,7 @@ type Element struct {
// to the corresponding type.
func NewElement(v interface{}) *Element {
return &Element{
value: makeStackItem(v),
value: stackitem.Make(v),
}
}
@ -60,12 +61,12 @@ func (e *Element) Prev() *Element {
return nil
}
// Item returns StackItem contained in the element.
func (e *Element) Item() StackItem {
// Item returns Item contained in the element.
func (e *Element) Item() stackitem.Item {
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{} {
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
// other items. Will panic if the item type is different which will be caught
// by the VM.
func (e *Element) Array() []StackItem {
func (e *Element) Array() []stackitem.Item {
switch t := e.value.(type) {
case *ArrayItem:
return t.value
case *StructItem:
return t.value
case *stackitem.Array:
return t.Value().([]stackitem.Item)
case *stackitem.Struct:
return t.Value().([]stackitem.Item)
default:
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
// as an interop item.
func (e *Element) Interop() *InteropItem {
func (e *Element) Interop() *stackitem.Interop {
switch t := e.value.(type) {
case *InteropItem:
case *stackitem.Interop:
return t
default:
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
// underlying StackItem to its corresponding type.
// underlying Item to its corresponding type.
func (s *Stack) PushVal(v interface{}) {
s.Push(NewElement(v))
}
@ -369,7 +370,7 @@ func (s *Stack) PopSigElements() ([][]byte, error) {
return nil, fmt.Errorf("nothing on the stack")
}
switch item.value.(type) {
case *ArrayItem:
case *stackitem.Array:
num = len(item.Array())
if num < 1 {
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())
s.IterBack(func(e *Element) {
// Each item is independent.
seen := make(map[StackItem]bool)
items = append(items, e.value.ToContractParameter(seen))
seen := make(map[stackitem.Item]bool)
items = append(items, smartcontract.ParameterFromStackItem(e.value, seen))
})
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"
"testing"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -231,9 +232,9 @@ func TestPushVal(t *testing.T) {
assert.Equal(t, true, elem.Bool())
// 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()
assert.IsType(t, elem.value, &ArrayItem{})
assert.IsType(t, elem.value, &stackitem.Array{})
}
func TestSwapElemValues(t *testing.T) {
@ -320,17 +321,17 @@ func TestPopSigElements(t *testing.T) {
_, err := s.PopSigElements()
assert.NotNil(t, err)
s.PushVal([]StackItem{})
s.PushVal([]stackitem.Item{})
_, err = s.PopSigElements()
assert.NotNil(t, err)
s.PushVal([]StackItem{NewBoolItem(false)})
s.PushVal([]stackitem.Item{stackitem.NewBool(false)})
_, err = s.PopSigElements()
assert.NotNil(t, err)
b1 := []byte("smth")
b2 := []byte("strange")
s.PushVal([]StackItem{NewByteArrayItem(b1), NewByteArrayItem(b2)})
s.PushVal([]stackitem.Item{stackitem.NewByteArray(b1), stackitem.NewByteArray(b2)})
z, err := s.PopSigElements()
assert.Nil(t, err)
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/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
// 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(emit.IntToBytes(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 := emit.BytesToInt(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

@ -15,6 +15,7 @@ import (
"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/stackitem"
"github.com/pkg/errors"
)
@ -52,14 +53,11 @@ const (
// MaxInvocationStackSize is the maximum size of an invocation stack.
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
// on all stacks at once.
MaxStackSize = 2 * 1024
maxSHLArg = MaxBigIntegerSizeBits
maxSHLArg = stackitem.MaxBigIntegerSizeBits
)
// 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.
func (v *VM) LoadArgs(method []byte, args []StackItem) {
func (v *VM) LoadArgs(method []byte, args []stackitem.Item) {
if len(args) > 0 {
v.estack.PushVal(args)
}
@ -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) {
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)
case opcode.PUSHNULL:
v.estack.PushVal(NullItem{})
v.estack.PushVal(stackitem.Null{})
case opcode.ISNULL:
res := v.estack.Pop().value.Equals(NullItem{})
res := v.estack.Pop().value.Equals(stackitem.Null{})
v.estack.PushVal(res)
case opcode.ISTYPE:
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:
typ := StackItemType(parameter[0])
typ := stackitem.Type(parameter[0])
item := v.estack.Pop().Item()
result, err := item.Convert(typ)
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 {
panic("invalid size")
}
v.estack.PushVal(NewBufferItem(make([]byte, n)))
v.estack.PushVal(stackitem.NewBuffer(make([]byte, n)))
case opcode.MEMCPY:
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 {
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) {
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))
}
ab := append(a, b...)
v.estack.PushVal(NewBufferItem(ab))
v.estack.PushVal(stackitem.NewBuffer(ab))
case opcode.SUBSTR:
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) {
panic("invalid offset")
}
v.estack.PushVal(NewBufferItem(s[o:last]))
v.estack.PushVal(stackitem.NewBuffer(s[o:last]))
case opcode.LEFT:
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 {
panic("size is too big")
}
v.estack.PushVal(NewBufferItem(s[:l]))
v.estack.PushVal(stackitem.NewBuffer(s[:l]))
case opcode.RIGHT:
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")
}
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:
v.estack.PushVal(v.estack.Len())
@ -801,7 +799,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// inplace
e := v.estack.Peek(0)
i := e.BigInt()
e.value = makeStackItem(i.Not(i))
e.value = stackitem.Make(i.Not(i))
case opcode.AND:
b := v.estack.Pop().BigInt()
@ -977,7 +975,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// Object operations
case opcode.NEWARRAY0:
v.estack.PushVal(&ArrayItem{[]StackItem{}})
v.estack.PushVal(stackitem.NewArray([]stackitem.Item{}))
case opcode.NEWARRAY, opcode.NEWARRAYT:
item := v.estack.Pop()
@ -985,15 +983,15 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n > MaxArraySize {
panic("too long array")
}
typ := AnyT
typ := stackitem.AnyT
if op == opcode.NEWARRAYT {
typ = StackItemType(parameter[0])
typ = stackitem.Type(parameter[0])
}
items := makeArrayOfType(int(n), typ)
v.estack.PushVal(&ArrayItem{items})
v.estack.PushVal(stackitem.NewArray(items))
case opcode.NEWSTRUCT0:
v.estack.PushVal(&StructItem{[]StackItem{}})
v.estack.PushVal(stackitem.NewStruct([]stackitem.Item{}))
case opcode.NEWSTRUCT:
item := v.estack.Pop()
@ -1001,8 +999,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n > MaxArraySize {
panic("too long struct")
}
items := makeArrayOfType(int(n), AnyT)
v.estack.PushVal(&StructItem{items})
items := makeArrayOfType(int(n), stackitem.AnyT)
v.estack.PushVal(stackitem.NewStruct(items))
case opcode.APPEND:
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)
switch t := arrElem.value.(type) {
case *ArrayItem:
arr := t.Value().([]StackItem)
if len(arr) >= MaxArraySize {
case *stackitem.Array:
if t.Len() >= MaxArraySize {
panic("too long array")
}
arr = append(arr, val)
t.value = arr
case *StructItem:
arr := t.Value().([]StackItem)
if len(arr) >= MaxArraySize {
t.Append(val)
case *stackitem.Struct:
if t.Len() >= MaxArraySize {
panic("too long struct")
}
arr = append(arr, val)
t.value = arr
t.Append(val)
default:
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")
}
items := make([]StackItem, n)
items := make([]stackitem.Item, n)
for i := 0; i < n; i++ {
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())
switch t := obj.value.(type) {
// Struct and Array items have their underlying value as []StackItem.
case *ArrayItem, *StructItem:
arr := t.Value().([]StackItem)
// Struct and Array items have their underlying value as []Item.
case *stackitem.Array, *stackitem.Struct:
arr := t.Value().([]stackitem.Item)
if index < 0 || index >= len(arr) {
panic("PICKITEM: invalid index")
}
item := arr[index].Dup()
v.estack.PushVal(item)
case *MapItem:
case *stackitem.Map:
index := t.Index(key.Item())
if index < 0 {
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:
arr := obj.Bytes()
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()
switch t := obj.value.(type) {
// Struct and Array items have their underlying value as []StackItem.
case *ArrayItem, *StructItem:
arr := t.Value().([]StackItem)
// Struct and Array items have their underlying value as []Item.
case *stackitem.Array, *stackitem.Struct:
arr := t.Value().([]stackitem.Item)
index := int(key.BigInt().Int64())
if index < 0 || index >= len(arr) {
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])
arr[index] = item
v.refs.Add(arr[index])
case *MapItem:
case *stackitem.Map:
if i := t.Index(key.value); i >= 0 {
v.refs.Remove(t.value[i].Value)
} else if len(t.value) >= MaxArraySize {
v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value)
} else if t.Len() >= MaxArraySize {
panic("too big map")
}
t.Add(key.value, item)
v.refs.Add(item)
case *BufferItem:
case *stackitem.Buffer:
index := toInt(key.BigInt())
if index < 0 || index >= len(t.value) {
if index < 0 || index >= t.Len() {
panic("invalid index")
}
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 {
panic("invalid value")
}
t.value[index] = byte(b)
t.Value().([]byte)[index] = byte(b)
default:
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:
item := v.estack.Pop()
switch t := item.value.(type) {
case *ArrayItem, *StructItem:
a := t.Value().([]StackItem)
case *stackitem.Array, *stackitem.Struct:
a := t.Value().([]stackitem.Item)
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
case *BufferItem:
for i, j := 0, len(t.value)-1; i < j; i, j = i+1, j-1 {
t.value[i], t.value[j] = t.value[j], t.value[i]
case *stackitem.Buffer:
for i, j := 0, t.Len()-1; i < j; i, j = i+1, j-1 {
t.Value().([]byte)[i], t.Value().([]byte)[j] = t.Value().([]byte)[j], t.Value().([]byte)[i]
}
default:
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()
switch t := elem.value.(type) {
case *ArrayItem:
a := t.value
case *stackitem.Array:
a := t.Value().([]stackitem.Item)
k := int(key.BigInt().Int64())
if k < 0 || k >= len(a) {
panic("REMOVE: invalid index")
}
v.refs.Remove(a[k])
a = append(a[:k], a[k+1:]...)
t.value = a
case *StructItem:
a := t.value
t.Remove(k)
case *stackitem.Struct:
a := t.Value().([]stackitem.Item)
k := int(key.BigInt().Int64())
if k < 0 || k >= len(a) {
panic("REMOVE: invalid index")
}
v.refs.Remove(a[k])
a = append(a[:k], a[k+1:]...)
t.value = a
case *MapItem:
t.Remove(k)
case *stackitem.Map:
index := t.Index(key.Item())
// NEO 2.0 doesn't error on missing key.
if index >= 0 {
v.refs.Remove(t.value[index].Value)
v.refs.Remove(t.Value().([]stackitem.MapElement)[index].Value)
t.Drop(index)
}
default:
@ -1179,21 +1171,21 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.CLEARITEMS:
elem := v.estack.Pop()
switch t := elem.value.(type) {
case *ArrayItem:
for _, item := range t.value {
case *stackitem.Array:
for _, item := range t.Value().([]stackitem.Item) {
v.refs.Remove(item)
}
t.value = t.value[:0]
case *StructItem:
for _, item := range t.value {
t.Clear()
case *stackitem.Struct:
for _, item := range t.Value().([]stackitem.Item) {
v.refs.Remove(item)
}
t.value = t.value[:0]
case *MapItem:
for i := range t.value {
v.refs.Remove(t.value[i].Value)
t.Clear()
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value)
}
t.value = t.value[:0]
t.Clear()
default:
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
// the type of the item for array size operations.
switch t := elem.Value().(type) {
case []StackItem:
case []stackitem.Item:
v.estack.PushVal(len(t))
case []MapElement:
case []stackitem.MapElement:
v.estack.PushVal(len(t))
default:
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)
case opcode.CALLA:
ptr := v.estack.Pop().Item().(*PointerItem)
if ptr.hash != ctx.ScriptHash() {
ptr := v.estack.Pop().Item().(*stackitem.Pointer)
if ptr.ScriptHash() != ctx.ScriptHash() {
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.rvcount = -1
v.istack.PushVal(newCtx)
v.jumpIf(newCtx, ptr.pos, true)
v.jumpIf(newCtx, ptr.Position(), true)
case opcode.SYSCALL:
interopID := GetInteropID(parameter)
@ -1291,7 +1283,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
}
case opcode.NEWMAP:
v.estack.Push(&Element{value: NewMapItem()})
v.estack.Push(&Element{value: stackitem.NewMap()})
case opcode.KEYS:
item := v.estack.Pop()
@ -1299,14 +1291,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("no argument")
}
m, ok := item.value.(*MapItem)
m, ok := item.value.(*stackitem.Map)
if !ok {
panic("not a Map")
}
arr := make([]StackItem, 0, len(m.value))
for k := range m.value {
arr = append(arr, m.value[k].Key.Dup())
arr := make([]stackitem.Item, 0, m.Len())
for k := range m.Value().([]stackitem.MapElement) {
arr = append(arr, m.Value().([]stackitem.MapElement)[k].Key.Dup())
}
v.estack.PushVal(arr)
@ -1316,18 +1308,18 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("no argument")
}
var arr []StackItem
var arr []stackitem.Item
switch t := item.value.(type) {
case *ArrayItem, *StructItem:
src := t.Value().([]StackItem)
arr = make([]StackItem, len(src))
case *stackitem.Array, *stackitem.Struct:
src := t.Value().([]stackitem.Item)
arr = make([]stackitem.Item, len(src))
for i := range src {
arr[i] = cloneIfStruct(src[i])
}
case *MapItem:
arr = make([]StackItem, 0, len(t.value))
for k := range t.value {
arr = append(arr, cloneIfStruct(t.value[k].Value))
case *stackitem.Map:
arr = make([]stackitem.Item, 0, t.Len())
for k := range t.Value().([]stackitem.MapElement) {
arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value))
}
default:
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")
}
switch t := c.value.(type) {
case *ArrayItem, *StructItem:
case *stackitem.Array, *stackitem.Struct:
index := key.BigInt().Int64()
if index < 0 {
panic("negative index")
}
v.estack.PushVal(index < int64(len(c.Array())))
case *MapItem:
case *stackitem.Map:
v.estack.PushVal(t.Has(key.Item()))
case *BufferItem:
case *stackitem.Buffer:
index := key.BigInt().Int64()
if index < 0 {
panic("negative index")
}
v.estack.PushVal(index < int64(len(t.value)))
v.estack.PushVal(index < int64(t.Len()))
default:
panic("wrong collection type")
}
@ -1536,30 +1528,30 @@ func checkMultisig1(v *VM, h []byte, pkeys [][]byte, sig []byte) bool {
return false
}
func cloneIfStruct(item StackItem) StackItem {
func cloneIfStruct(item stackitem.Item) stackitem.Item {
switch it := item.(type) {
case *StructItem:
case *stackitem.Struct:
return it.Clone()
default:
return it
}
}
func makeArrayOfType(n int, typ StackItemType) []StackItem {
func makeArrayOfType(n int, typ stackitem.Type) []stackitem.Item {
if !typ.IsValid() {
panic(fmt.Sprintf("invalid stack item type: %d", typ))
}
items := make([]StackItem, n)
items := make([]stackitem.Item, n)
for i := range items {
switch typ {
case BooleanT:
items[i] = NewBoolItem(false)
case IntegerT:
items[i] = NewBigIntegerItem(big.NewInt(0))
case ByteArrayT:
items[i] = NewByteArrayItem([]byte{})
case stackitem.BooleanT:
items[i] = stackitem.NewBool(false)
case stackitem.IntegerT:
items[i] = stackitem.NewBigInteger(big.NewInt(0))
case stackitem.ByteArrayT:
items[i] = stackitem.NewByteArray([]byte{})
default:
items[i] = NullItem{}
items[i] = stackitem.Null{}
}
}
return items
@ -1569,7 +1561,7 @@ func validateMapKey(key *Element) {
if key == nil {
panic("no key found")
}
if !isValidMapKey(key.Item()) {
if !stackitem.IsValidMapKey(key.Item()) {
panic("key can't be a collection")
}
}

File diff suppressed because it is too large Load diff