forked from TrueCloudLab/neoneo-go
Merge pull request #1013 from nspcc-dev/neo3/vm/stackitem_refactoring
vm: move StackItem to a separate package
This commit is contained in:
commit
795523f5cd
50 changed files with 2575 additions and 2466 deletions
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -720,7 +721,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
// For now we will assume that there are only byte slice conversions.
|
// For now we will assume that there are only byte slice conversions.
|
||||||
// E.g. []byte("foobar") or []byte(scriptHash).
|
// E.g. []byte("foobar") or []byte(scriptHash).
|
||||||
ast.Walk(c, n.Args[0])
|
ast.Walk(c, n.Args[0])
|
||||||
c.emitConvert(vm.BufferT)
|
c.emitConvert(stackitem.BufferT)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,12 +1092,12 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
c.prog.Err = errors.New("panic should have string or nil argument")
|
c.prog.Err = errors.New("panic should have string or nil argument")
|
||||||
}
|
}
|
||||||
case "ToInteger", "ToByteArray", "ToBool":
|
case "ToInteger", "ToByteArray", "ToBool":
|
||||||
typ := vm.IntegerT
|
typ := stackitem.IntegerT
|
||||||
switch name {
|
switch name {
|
||||||
case "ToByteArray":
|
case "ToByteArray":
|
||||||
typ = vm.ByteArrayT
|
typ = stackitem.ByteArrayT
|
||||||
case "ToBool":
|
case "ToBool":
|
||||||
typ = vm.BooleanT
|
typ = stackitem.BooleanT
|
||||||
}
|
}
|
||||||
c.emitConvert(typ)
|
c.emitConvert(typ)
|
||||||
case "SHA256":
|
case "SHA256":
|
||||||
|
@ -1123,7 +1124,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
}
|
}
|
||||||
bytes := uint160.BytesBE()
|
bytes := uint160.BytesBE()
|
||||||
emit.Bytes(c.prog.BinWriter, bytes)
|
emit.Bytes(c.prog.BinWriter, bytes)
|
||||||
c.emitConvert(vm.BufferT)
|
c.emitConvert(stackitem.BufferT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1150,7 +1151,7 @@ func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitConvert converts top stack item to the specified type.
|
// emitConvert converts top stack item to the specified type.
|
||||||
func (c *codegen) emitConvert(typ vm.StackItemType) {
|
func (c *codegen) emitConvert(typ stackitem.Type) {
|
||||||
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
|
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1162,7 +1163,7 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
||||||
buf[i] = byte(val)
|
buf[i] = byte(val)
|
||||||
}
|
}
|
||||||
emit.Bytes(c.prog.BinWriter, buf)
|
emit.Bytes(c.prog.BinWriter, buf)
|
||||||
c.emitConvert(vm.BufferT)
|
c.emitConvert(stackitem.BufferT)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertMap(lit *ast.CompositeLit) {
|
func (c *codegen) convertMap(lit *ast.CompositeLit) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEntryPointWithMethod(t *testing.T) {
|
func TestEntryPointWithMethod(t *testing.T) {
|
||||||
|
@ -30,7 +30,7 @@ func TestEntryPointWithArgs(t *testing.T) {
|
||||||
return 2 + args[1].(int)
|
return 2 + args[1].(int)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
args := []vm.StackItem{vm.NewBigIntegerItem(big.NewInt(0)), vm.NewBigIntegerItem(big.NewInt(1))}
|
args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))}
|
||||||
evalWithArgs(t, src, nil, args, big.NewInt(3))
|
evalWithArgs(t, src, nil, args, big.NewInt(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ func TestEntryPointWithMethodAndArgs(t *testing.T) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
args := []vm.StackItem{vm.NewBigIntegerItem(big.NewInt(0)), vm.NewBigIntegerItem(big.NewInt(1))}
|
args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))}
|
||||||
evalWithArgs(t, src, []byte("foobar"), args, big.NewInt(3))
|
evalWithArgs(t, src, []byte("foobar"), args, big.NewInt(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,10 +134,10 @@ func TestStringArray(t *testing.T) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
eval(t, src, []vm.StackItem{
|
eval(t, src, []stackitem.Item{
|
||||||
vm.NewByteArrayItem([]byte("foo")),
|
stackitem.NewByteArray([]byte("foo")),
|
||||||
vm.NewByteArrayItem([]byte("bar")),
|
stackitem.NewByteArray([]byte("bar")),
|
||||||
vm.NewByteArrayItem([]byte("foobar")),
|
stackitem.NewByteArray([]byte("foobar")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,10 +149,10 @@ func TestIntArray(t *testing.T) {
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
eval(t, src, []vm.StackItem{
|
eval(t, src, []stackitem.Item{
|
||||||
vm.NewBigIntegerItem(big.NewInt(1)),
|
stackitem.NewBigInteger(big.NewInt(1)),
|
||||||
vm.NewBigIntegerItem(big.NewInt(2)),
|
stackitem.NewBigInteger(big.NewInt(2)),
|
||||||
vm.NewBigIntegerItem(big.NewInt(3)),
|
stackitem.NewBigInteger(big.NewInt(3)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ func TestSimpleString(t *testing.T) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
eval(t, src, vm.NewByteArrayItem([]byte("NEO")).Value())
|
eval(t, src, stackitem.NewByteArray([]byte("NEO")).Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBoolAssign(t *testing.T) {
|
func TestBoolAssign(t *testing.T) {
|
||||||
|
@ -315,7 +315,7 @@ func TestAppendString(t *testing.T) {
|
||||||
return arr[3]
|
return arr[3]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
eval(t, src, vm.NewByteArrayItem([]byte("d")).Value())
|
eval(t, src, stackitem.NewByteArray([]byte("d")).Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppendInt(t *testing.T) {
|
func TestAppendInt(t *testing.T) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sliceTestCases = []testCase{
|
var sliceTestCases = []testCase{
|
||||||
|
@ -160,9 +160,9 @@ var sliceTestCases = []testCase{
|
||||||
a = append(a, "b")
|
a = append(a, "b")
|
||||||
return a
|
return a
|
||||||
}`,
|
}`,
|
||||||
[]vm.StackItem{
|
[]stackitem.Item{
|
||||||
vm.NewByteArrayItem([]byte("a")),
|
stackitem.NewByteArray([]byte("a")),
|
||||||
vm.NewByteArrayItem([]byte("b")),
|
stackitem.NewByteArray([]byte("b")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -175,9 +175,9 @@ var sliceTestCases = []testCase{
|
||||||
a = append(a, "b")
|
a = append(a, "b")
|
||||||
return a
|
return a
|
||||||
}`,
|
}`,
|
||||||
[]vm.StackItem{
|
[]stackitem.Item{
|
||||||
vm.NewByteArrayItem([]byte("a")),
|
stackitem.NewByteArray([]byte("a")),
|
||||||
vm.NewByteArrayItem([]byte("b")),
|
stackitem.NewByteArray([]byte("b")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
var structTestCases = []testCase{
|
var structTestCases = []testCase{
|
||||||
|
@ -281,11 +281,11 @@ var structTestCases = []testCase{
|
||||||
return newToken()
|
return newToken()
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
[]vm.StackItem{
|
[]stackitem.Item{
|
||||||
vm.NewBigIntegerItem(big.NewInt(1)),
|
stackitem.NewBigInteger(big.NewInt(1)),
|
||||||
vm.NewBigIntegerItem(big.NewInt(2)),
|
stackitem.NewBigInteger(big.NewInt(2)),
|
||||||
vm.NewByteArrayItem([]byte("hello")),
|
stackitem.NewByteArray([]byte("hello")),
|
||||||
vm.NewBoolItem(false),
|
stackitem.NewBool(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -41,8 +41,8 @@ func TestNotify(t *testing.T) {
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, v.Run())
|
||||||
require.Equal(t, 3, len(s.events))
|
require.Equal(t, 3, len(s.events))
|
||||||
|
|
||||||
exp0 := []vm.StackItem{vm.NewBigIntegerItem(big.NewInt(11)), vm.NewByteArrayItem([]byte("sum")), vm.NewBigIntegerItem(big.NewInt(12))}
|
exp0 := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(11)), stackitem.NewByteArray([]byte("sum")), stackitem.NewBigInteger(big.NewInt(12))}
|
||||||
assert.Equal(t, exp0, s.events[0].Value())
|
assert.Equal(t, exp0, s.events[0].Value())
|
||||||
assert.Equal(t, []vm.StackItem{}, s.events[1].Value())
|
assert.Equal(t, []stackitem.Item{}, s.events[1].Value())
|
||||||
assert.Equal(t, []vm.StackItem{vm.NewByteArrayItem([]byte("single"))}, s.events[2].Value())
|
assert.Equal(t, []stackitem.Item{stackitem.NewByteArray([]byte("single"))}, s.events[2].Value())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -38,7 +39,7 @@ func eval(t *testing.T, src string, result interface{}) {
|
||||||
assertResult(t, vm, result)
|
assertResult(t, vm, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalWithArgs(t *testing.T, src string, op []byte, args []vm.StackItem, result interface{}) {
|
func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, result interface{}) {
|
||||||
vm := vmAndCompile(t, src)
|
vm := vmAndCompile(t, src)
|
||||||
vm.LoadArgs(op, args)
|
vm.LoadArgs(op, args)
|
||||||
err := vm.Run()
|
err := vm.Run()
|
||||||
|
@ -73,7 +74,7 @@ func vmAndCompileInterop(t *testing.T, src string) (*vm.VM, *storagePlugin) {
|
||||||
type storagePlugin struct {
|
type storagePlugin struct {
|
||||||
mem map[string][]byte
|
mem map[string][]byte
|
||||||
interops map[uint32]vm.InteropFunc
|
interops map[uint32]vm.InteropFunc
|
||||||
events []vm.StackItem
|
events []stackitem.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStoragePlugin() *storagePlugin {
|
func newStoragePlugin() *storagePlugin {
|
||||||
|
|
|
@ -18,11 +18,13 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -573,7 +575,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
return errors.Wrap(err, "failed to persist invocation results")
|
return errors.Wrap(err, "failed to persist invocation results")
|
||||||
}
|
}
|
||||||
for _, note := range systemInterop.Notifications {
|
for _, note := range systemInterop.Notifications {
|
||||||
arr, ok := note.Item.Value().([]vm.StackItem)
|
arr, ok := note.Item.Value().([]stackitem.Item)
|
||||||
if !ok || len(arr) != 4 {
|
if !ok || len(arr) != 4 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -605,7 +607,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
amount = emit.BytesToInt(bs)
|
amount = bigint.FromBytes(bs)
|
||||||
}
|
}
|
||||||
bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64())
|
bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64())
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ type Function struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method is a signature for a native method.
|
// Method is a signature for a native method.
|
||||||
type Method = func(ic *Context, args []vm.StackItem) vm.StackItem
|
type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
|
||||||
|
|
||||||
// MethodAndPrice is a native-contract method descriptor.
|
// MethodAndPrice is a native-contract method descriptor.
|
||||||
type MethodAndPrice struct {
|
type MethodAndPrice struct {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ECDSAVerify checks ECDSA signature.
|
// ECDSAVerify checks ECDSA signature.
|
||||||
|
@ -48,12 +49,12 @@ func ECDSACheckMultisig(ic *interop.Context, v *vm.VM) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMessage(ic *interop.Context, item vm.StackItem) []byte {
|
func getMessage(ic *interop.Context, item stackitem.Item) []byte {
|
||||||
var msg []byte
|
var msg []byte
|
||||||
switch val := item.(type) {
|
switch val := item.(type) {
|
||||||
case *vm.InteropItem:
|
case *stackitem.Interop:
|
||||||
msg = val.Value().(crypto.Verifiable).GetSignedPart()
|
msg = val.Value().(crypto.Verifiable).GetSignedPart()
|
||||||
case vm.NullItem:
|
case stackitem.Null:
|
||||||
msg = ic.Container.GetSignedPart()
|
msg = ic.Container.GetSignedPart()
|
||||||
default:
|
default:
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -9,16 +9,17 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initCHECKMULTISIG(msg []byte, n int) ([]vm.StackItem, []vm.StackItem, map[string]*keys.PublicKey, error) {
|
func initCHECKMULTISIG(msg []byte, n int) ([]stackitem.Item, []stackitem.Item, map[string]*keys.PublicKey, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
keyMap := make(map[string]*keys.PublicKey)
|
keyMap := make(map[string]*keys.PublicKey)
|
||||||
pkeys := make([]*keys.PrivateKey, n)
|
pkeys := make([]*keys.PrivateKey, n)
|
||||||
pubs := make([]vm.StackItem, n)
|
pubs := make([]stackitem.Item, n)
|
||||||
for i := range pubs {
|
for i := range pubs {
|
||||||
pkeys[i], err = keys.NewPrivateKey()
|
pkeys[i], err = keys.NewPrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,25 +28,25 @@ func initCHECKMULTISIG(msg []byte, n int) ([]vm.StackItem, []vm.StackItem, map[s
|
||||||
|
|
||||||
pk := pkeys[i].PublicKey()
|
pk := pkeys[i].PublicKey()
|
||||||
data := pk.Bytes()
|
data := pk.Bytes()
|
||||||
pubs[i] = vm.NewByteArrayItem(data)
|
pubs[i] = stackitem.NewByteArray(data)
|
||||||
keyMap[string(data)] = pk
|
keyMap[string(data)] = pk
|
||||||
}
|
}
|
||||||
|
|
||||||
sigs := make([]vm.StackItem, n)
|
sigs := make([]stackitem.Item, n)
|
||||||
for i := range sigs {
|
for i := range sigs {
|
||||||
sig := pkeys[i].Sign(msg)
|
sig := pkeys[i].Sign(msg)
|
||||||
sigs[i] = vm.NewByteArrayItem(sig)
|
sigs[i] = stackitem.NewByteArray(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pubs, sigs, keyMap, nil
|
return pubs, sigs, keyMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func subSlice(arr []vm.StackItem, indices []int) []vm.StackItem {
|
func subSlice(arr []stackitem.Item, indices []int) []stackitem.Item {
|
||||||
if indices == nil {
|
if indices == nil {
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]vm.StackItem, len(indices))
|
result := make([]stackitem.Item, len(indices))
|
||||||
for i, j := range indices {
|
for i, j := range indices {
|
||||||
result[i] = arr[j]
|
result[i] = arr[j]
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -66,9 +67,9 @@ func txGetAttributes(ic *interop.Context, v *vm.VM) error {
|
||||||
if len(tx.Attributes) > vm.MaxArraySize {
|
if len(tx.Attributes) > vm.MaxArraySize {
|
||||||
return errors.New("too many attributes")
|
return errors.New("too many attributes")
|
||||||
}
|
}
|
||||||
attrs := make([]vm.StackItem, 0, len(tx.Attributes))
|
attrs := make([]stackitem.Item, 0, len(tx.Attributes))
|
||||||
for i := range tx.Attributes {
|
for i := range tx.Attributes {
|
||||||
attrs = append(attrs, vm.NewInteropItem(&tx.Attributes[i]))
|
attrs = append(attrs, stackitem.NewInterop(&tx.Attributes[i]))
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(attrs)
|
v.Estack().PushVal(attrs)
|
||||||
return nil
|
return nil
|
||||||
|
@ -84,9 +85,9 @@ func txGetWitnesses(ic *interop.Context, v *vm.VM) error {
|
||||||
if len(tx.Scripts) > vm.MaxArraySize {
|
if len(tx.Scripts) > vm.MaxArraySize {
|
||||||
return errors.New("too many outputs")
|
return errors.New("too many outputs")
|
||||||
}
|
}
|
||||||
scripts := make([]vm.StackItem, 0, len(tx.Scripts))
|
scripts := make([]stackitem.Item, 0, len(tx.Scripts))
|
||||||
for i := range tx.Scripts {
|
for i := range tx.Scripts {
|
||||||
scripts = append(scripts, vm.NewInteropItem(&tx.Scripts[i]))
|
scripts = append(scripts, stackitem.NewInterop(&tx.Scripts[i]))
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(scripts)
|
v.Estack().PushVal(scripts)
|
||||||
return nil
|
return nil
|
||||||
|
@ -139,7 +140,7 @@ func bcGetAccount(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(acc))
|
v.Estack().PushVal(stackitem.NewInterop(acc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,13 +205,13 @@ func storageFind(ic *interop.Context, v *vm.VM) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredMap := vm.NewMapItem()
|
filteredMap := stackitem.NewMap()
|
||||||
for k, v := range siMap {
|
for k, v := range siMap {
|
||||||
filteredMap.Add(vm.NewByteArrayItem(append(prefix, []byte(k)...)), vm.NewByteArrayItem(v.Value))
|
filteredMap.Add(stackitem.NewByteArray(append(prefix, []byte(k)...)), stackitem.NewByteArray(v.Value))
|
||||||
}
|
}
|
||||||
sort.Slice(filteredMap.Value().([]vm.MapElement), func(i, j int) bool {
|
sort.Slice(filteredMap.Value().([]stackitem.MapElement), func(i, j int) bool {
|
||||||
return bytes.Compare(filteredMap.Value().([]vm.MapElement)[i].Key.Value().([]byte),
|
return bytes.Compare(filteredMap.Value().([]stackitem.MapElement)[i].Key.Value().([]byte),
|
||||||
filteredMap.Value().([]vm.MapElement)[j].Key.Value().([]byte)) == -1
|
filteredMap.Value().([]stackitem.MapElement)[j].Key.Value().([]byte)) == -1
|
||||||
})
|
})
|
||||||
|
|
||||||
item := vm.NewMapIterator(filteredMap)
|
item := vm.NewMapIterator(filteredMap)
|
||||||
|
@ -288,7 +289,7 @@ func contractCreate(ic *interop.Context, v *vm.VM) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(contract))
|
v.Estack().PushVal(stackitem.NewInterop(contract))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +343,7 @@ func contractMigrate(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(contract))
|
v.Estack().PushVal(stackitem.NewInterop(contract))
|
||||||
return contractDestroy(ic, v)
|
return contractDestroy(ic, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,12 +71,12 @@ func TestStorageFind(t *testing.T) {
|
||||||
|
|
||||||
t.Run("normal invocation", func(t *testing.T) {
|
t.Run("normal invocation", func(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{0x01})
|
v.Estack().PushVal([]byte{0x01})
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: scriptHash}))
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: scriptHash}))
|
||||||
|
|
||||||
err := storageFind(context, v)
|
err := storageFind(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var iter *vm.InteropItem
|
var iter *stackitem.Interop
|
||||||
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
|
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
|
||||||
|
|
||||||
require.NoError(t, enumerator.Next(context, v))
|
require.NoError(t, enumerator.Next(context, v))
|
||||||
|
@ -108,7 +109,7 @@ func TestStorageFind(t *testing.T) {
|
||||||
|
|
||||||
t.Run("normal invocation, empty result", func(t *testing.T) {
|
t.Run("normal invocation, empty result", func(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{0x03})
|
v.Estack().PushVal([]byte{0x03})
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: scriptHash}))
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: scriptHash}))
|
||||||
|
|
||||||
err := storageFind(context, v)
|
err := storageFind(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -119,7 +120,7 @@ func TestStorageFind(t *testing.T) {
|
||||||
|
|
||||||
t.Run("invalid type for StorageContext", func(t *testing.T) {
|
t.Run("invalid type for StorageContext", func(t *testing.T) {
|
||||||
v.Estack().PushVal([]byte{0x01})
|
v.Estack().PushVal([]byte{0x01})
|
||||||
v.Estack().PushVal(vm.NewInteropItem(nil))
|
v.Estack().PushVal(stackitem.NewInterop(nil))
|
||||||
|
|
||||||
require.Error(t, storageFind(context, v))
|
require.Error(t, storageFind(context, v))
|
||||||
})
|
})
|
||||||
|
@ -129,7 +130,7 @@ func TestStorageFind(t *testing.T) {
|
||||||
invalidHash[0] = ^invalidHash[0]
|
invalidHash[0] = ^invalidHash[0]
|
||||||
|
|
||||||
v.Estack().PushVal([]byte{0x01})
|
v.Estack().PushVal([]byte{0x01})
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: invalidHash}))
|
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ScriptHash: invalidHash}))
|
||||||
|
|
||||||
require.Error(t, storageFind(context, v))
|
require.Error(t, storageFind(context, v))
|
||||||
})
|
})
|
||||||
|
@ -151,7 +152,7 @@ func TestHeaderGetVersion_Negative(t *testing.T) {
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
|
||||||
v.Estack().PushVal(vm.NewBoolItem(false))
|
v.Estack().PushVal(stackitem.NewBool(false))
|
||||||
|
|
||||||
err := headerGetVersion(context, v)
|
err := headerGetVersion(context, v)
|
||||||
require.Errorf(t, err, "value is not a header or block")
|
require.Errorf(t, err, "value is not a header or block")
|
||||||
|
@ -183,7 +184,7 @@ func TestTxGetAttributes(t *testing.T) {
|
||||||
|
|
||||||
err := txGetAttributes(context, v)
|
err := txGetAttributes(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
value := v.Estack().Pop().Value().([]vm.StackItem)
|
value := v.Estack().Pop().Value().([]stackitem.Item)
|
||||||
require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage)
|
require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ func TestWitnessGetVerificationScript(t *testing.T) {
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&witness))
|
v.Estack().PushVal(stackitem.NewInterop(&witness))
|
||||||
err := witnessGetVerificationScript(context, v)
|
err := witnessGetVerificationScript(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
value := v.Estack().Pop().Value().([]byte)
|
value := v.Estack().Pop().Value().([]byte)
|
||||||
|
@ -247,7 +248,7 @@ func TestECDSAVerify(t *testing.T) {
|
||||||
tx := transaction.New([]byte{0, 1, 2}, 1)
|
tx := transaction.New([]byte{0, 1, 2}, 1)
|
||||||
msg := tx.GetSignedPart()
|
msg := tx.GetSignedPart()
|
||||||
sign := priv.Sign(msg)
|
sign := priv.Sign(msg)
|
||||||
runCase(t, false, true, sign, priv.PublicKey().Bytes(), vm.NewInteropItem(tx))
|
runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.NewInterop(tx))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("signed script container", func(t *testing.T) {
|
t.Run("signed script container", func(t *testing.T) {
|
||||||
|
@ -255,7 +256,7 @@ func TestECDSAVerify(t *testing.T) {
|
||||||
msg := tx.GetSignedPart()
|
msg := tx.GetSignedPart()
|
||||||
sign := priv.Sign(msg)
|
sign := priv.Sign(msg)
|
||||||
ic.Container = tx
|
ic.Container = tx
|
||||||
runCase(t, false, true, sign, priv.PublicKey().Bytes(), vm.NullItem{})
|
runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.Null{})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("missing arguments", func(t *testing.T) {
|
t.Run("missing arguments", func(t *testing.T) {
|
||||||
|
@ -282,7 +283,7 @@ func TestECDSAVerify(t *testing.T) {
|
||||||
func TestAttrGetData(t *testing.T) {
|
func TestAttrGetData(t *testing.T) {
|
||||||
v, tx, context, chain := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
|
v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))
|
||||||
|
|
||||||
err := attrGetData(context, v)
|
err := attrGetData(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -293,7 +294,7 @@ func TestAttrGetData(t *testing.T) {
|
||||||
func TestAttrGetUsage(t *testing.T) {
|
func TestAttrGetUsage(t *testing.T) {
|
||||||
v, tx, context, chain := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Attributes[0]))
|
v.Estack().PushVal(stackitem.NewInterop(&tx.Attributes[0]))
|
||||||
|
|
||||||
err := attrGetUsage(context, v)
|
err := attrGetUsage(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -304,7 +305,7 @@ func TestAttrGetUsage(t *testing.T) {
|
||||||
func TestAccountGetScriptHash(t *testing.T) {
|
func TestAccountGetScriptHash(t *testing.T) {
|
||||||
v, accState, context, chain := createVMAndAccState(t)
|
v, accState, context, chain := createVMAndAccState(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(accState))
|
v.Estack().PushVal(stackitem.NewInterop(accState))
|
||||||
|
|
||||||
err := accountGetScriptHash(context, v)
|
err := accountGetScriptHash(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -315,7 +316,7 @@ func TestAccountGetScriptHash(t *testing.T) {
|
||||||
func TestContractGetScript(t *testing.T) {
|
func TestContractGetScript(t *testing.T) {
|
||||||
v, contractState, context, chain := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(contractState))
|
v.Estack().PushVal(stackitem.NewInterop(contractState))
|
||||||
|
|
||||||
err := contractGetScript(context, v)
|
err := contractGetScript(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -326,7 +327,7 @@ func TestContractGetScript(t *testing.T) {
|
||||||
func TestContractIsPayable(t *testing.T) {
|
func TestContractIsPayable(t *testing.T) {
|
||||||
v, contractState, context, chain := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
v.Estack().PushVal(vm.NewInteropItem(contractState))
|
v.Estack().PushVal(stackitem.NewInterop(contractState))
|
||||||
|
|
||||||
err := contractIsPayable(context, v)
|
err := contractIsPayable(context, v)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -341,13 +342,13 @@ func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context,
|
||||||
block := newDumbBlock()
|
block := newDumbBlock()
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), block, nil)
|
||||||
v.Estack().PushVal(vm.NewInteropItem(block))
|
v.Estack().PushVal(stackitem.NewInterop(block))
|
||||||
return v, block, context, chain
|
return v, block, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
|
func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
|
||||||
v, tx, context, chain := createVMAndTX(t)
|
v, tx, context, chain := createVMAndTX(t)
|
||||||
v.Estack().PushVal(vm.NewInteropItem(tx))
|
v.Estack().PushVal(stackitem.NewInterop(tx))
|
||||||
return v, tx, context, chain
|
return v, tx, context, chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ func bcGetBlock(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Estack().PushVal([]byte{})
|
v.Estack().PushVal([]byte{})
|
||||||
} else {
|
} else {
|
||||||
v.Estack().PushVal(vm.NewInteropItem(block))
|
v.Estack().PushVal(stackitem.NewInterop(block))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -74,7 +75,7 @@ func bcGetContract(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Estack().PushVal([]byte{})
|
v.Estack().PushVal([]byte{})
|
||||||
} else {
|
} else {
|
||||||
v.Estack().PushVal(vm.NewInteropItem(cs))
|
v.Estack().PushVal(stackitem.NewInterop(cs))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -89,7 +90,7 @@ func bcGetHeader(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Estack().PushVal([]byte{})
|
v.Estack().PushVal([]byte{})
|
||||||
} else {
|
} else {
|
||||||
v.Estack().PushVal(vm.NewInteropItem(header))
|
v.Estack().PushVal(stackitem.NewInterop(header))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,7 @@ func bcGetTransaction(ic *interop.Context, v *vm.VM) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(tx))
|
v.Estack().PushVal(stackitem.NewInterop(tx))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,9 +209,9 @@ func blockGetTransactions(ic *interop.Context, v *vm.VM) error {
|
||||||
if len(block.Transactions) > vm.MaxArraySize {
|
if len(block.Transactions) > vm.MaxArraySize {
|
||||||
return errors.New("too many transactions")
|
return errors.New("too many transactions")
|
||||||
}
|
}
|
||||||
txes := make([]vm.StackItem, 0, len(block.Transactions))
|
txes := make([]stackitem.Item, 0, len(block.Transactions))
|
||||||
for _, tx := range block.Transactions {
|
for _, tx := range block.Transactions {
|
||||||
txes = append(txes, vm.NewInteropItem(tx))
|
txes = append(txes, stackitem.NewInterop(tx))
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(txes)
|
v.Estack().PushVal(txes)
|
||||||
return nil
|
return nil
|
||||||
|
@ -229,7 +230,7 @@ func blockGetTransaction(ic *interop.Context, v *vm.VM) error {
|
||||||
return errors.New("wrong transaction index")
|
return errors.New("wrong transaction index")
|
||||||
}
|
}
|
||||||
tx := block.Transactions[index]
|
tx := block.Transactions[index]
|
||||||
v.Estack().PushVal(vm.NewInteropItem(tx))
|
v.Estack().PushVal(stackitem.NewInterop(tx))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +248,7 @@ func txGetHash(ic *interop.Context, v *vm.VM) error {
|
||||||
// engineGetScriptContainer returns transaction that contains the script being
|
// engineGetScriptContainer returns transaction that contains the script being
|
||||||
// run.
|
// run.
|
||||||
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
func engineGetScriptContainer(ic *interop.Context, v *vm.VM) error {
|
||||||
v.Estack().PushVal(vm.NewInteropItem(ic.Container))
|
v.Estack().PushVal(stackitem.NewInterop(ic.Container))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,9 +290,9 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
|
||||||
// outside of the interop subsystem anyway. I'd probably fail transactions
|
// outside of the interop subsystem anyway. I'd probably fail transactions
|
||||||
// that emit such broken notifications, but that might break compatibility
|
// that emit such broken notifications, but that might break compatibility
|
||||||
// with testnet/mainnet, so we're replacing these with error messages.
|
// with testnet/mainnet, so we're replacing these with error messages.
|
||||||
_, err := vm.SerializeItem(item)
|
_, err := stackitem.SerializeItem(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
item = vm.NewByteArrayItem([]byte(fmt.Sprintf("bad notification: %v", err)))
|
item = stackitem.NewByteArray([]byte(fmt.Sprintf("bad notification: %v", err)))
|
||||||
}
|
}
|
||||||
ne := state.NotificationEvent{ScriptHash: v.GetCurrentScriptHash(), Item: item}
|
ne := state.NotificationEvent{ScriptHash: v.GetCurrentScriptHash(), Item: item}
|
||||||
ic.Notifications = append(ic.Notifications, ne)
|
ic.Notifications = append(ic.Notifications, ne)
|
||||||
|
@ -387,7 +388,7 @@ func storageGetContext(ic *interop.Context, v *vm.VM) error {
|
||||||
ScriptHash: v.GetCurrentScriptHash(),
|
ScriptHash: v.GetCurrentScriptHash(),
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(sc))
|
v.Estack().PushVal(stackitem.NewInterop(sc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,7 +398,7 @@ func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
|
||||||
ScriptHash: v.GetCurrentScriptHash(),
|
ScriptHash: v.GetCurrentScriptHash(),
|
||||||
ReadOnly: true,
|
ReadOnly: true,
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(sc))
|
v.Estack().PushVal(stackitem.NewInterop(sc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +468,7 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
|
||||||
}
|
}
|
||||||
stc = stx
|
stc = stx
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(stc))
|
v.Estack().PushVal(stackitem.NewInterop(stc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,7 +489,7 @@ func contractCallEx(ic *interop.Context, v *vm.VM) error {
|
||||||
return contractCallExInternal(ic, v, h, method, args, flags)
|
return contractCallExInternal(ic, v, h, method, args, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method vm.StackItem, args vm.StackItem, _ smartcontract.CallFlag) error {
|
func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method stackitem.Item, args stackitem.Item, _ smartcontract.CallFlag) error {
|
||||||
u, err := util.Uint160DecodeBytesBE(h)
|
u, err := util.Uint160DecodeBytesBE(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("invalid contract hash")
|
return errors.New("invalid contract hash")
|
||||||
|
@ -548,6 +549,6 @@ func contractGetStorageContext(ic *interop.Context, v *vm.VM) error {
|
||||||
stc := &StorageContext{
|
stc := &StorageContext{
|
||||||
ScriptHash: cs.ScriptHash(),
|
ScriptHash: cs.ScriptHash(),
|
||||||
}
|
}
|
||||||
v.Estack().PushVal(vm.NewInteropItem(stc))
|
v.Estack().PushVal(stackitem.NewInterop(stc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) unclaimedGas(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
u := toUint160(args[0])
|
u := toUint160(args[0])
|
||||||
end := uint32(toBigInt(args[1]).Int64())
|
end := uint32(toBigInt(args[1]).Int64())
|
||||||
bs, err := ic.DAO.GetNEP5Balances(u)
|
bs, err := ic.DAO.GetNEP5Balances(u)
|
||||||
|
@ -204,12 +204,12 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []vm.StackItem) vm.StackIte
|
||||||
tr := bs.Trackers[n.Hash]
|
tr := bs.Trackers[n.Hash]
|
||||||
|
|
||||||
gen := ic.Chain.CalculateClaimable(tr.Balance, tr.LastUpdatedBlock, end)
|
gen := ic.Chain.CalculateClaimable(tr.Balance, tr.LastUpdatedBlock, end)
|
||||||
return vm.NewBigIntegerItem(big.NewInt(int64(gen)))
|
return stackitem.NewBigInteger(big.NewInt(int64(gen)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) registerValidator(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (n *NEO) registerValidator(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
err := n.registerValidatorInternal(ic, toPublicKey(args[0]))
|
err := n.registerValidatorInternal(ic, toPublicKey(args[0]))
|
||||||
return vm.NewBoolItem(err == nil)
|
return stackitem.NewBool(err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
||||||
|
@ -226,9 +226,9 @@ func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey
|
||||||
return ic.DAO.PutStorageItem(n.Hash, key, si)
|
return ic.DAO.PutStorageItem(n.Hash, key, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
acc := toUint160(args[0])
|
acc := toUint160(args[0])
|
||||||
arr := args[1].Value().([]vm.StackItem)
|
arr := args[1].Value().([]stackitem.Item)
|
||||||
var pubs keys.PublicKeys
|
var pubs keys.PublicKeys
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
pub := new(keys.PublicKey)
|
pub := new(keys.PublicKey)
|
||||||
|
@ -241,7 +241,7 @@ func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||||
pubs = append(pubs, pub)
|
pubs = append(pubs, pub)
|
||||||
}
|
}
|
||||||
err := n.VoteInternal(ic, acc, pubs)
|
err := n.VoteInternal(ic, acc, pubs)
|
||||||
return vm.NewBoolItem(err == nil)
|
return stackitem.NewBool(err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VoteInternal votes from account h for validarors specified in pubs.
|
// VoteInternal votes from account h for validarors specified in pubs.
|
||||||
|
@ -361,19 +361,19 @@ func (n *NEO) GetRegisteredValidators(d dao.DAO) ([]state.Validator, error) {
|
||||||
return arr, nil
|
return arr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
validators, err := n.getRegisteredValidators(ic.DAO)
|
validators, err := n.getRegisteredValidators(ic.DAO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
arr := make([]vm.StackItem, len(validators))
|
arr := make([]stackitem.Item, len(validators))
|
||||||
for i := range validators {
|
for i := range validators {
|
||||||
arr[i] = vm.NewStructItem([]vm.StackItem{
|
arr[i] = stackitem.NewStruct([]stackitem.Item{
|
||||||
vm.NewByteArrayItem([]byte(validators[i].Key)),
|
stackitem.NewByteArray([]byte(validators[i].Key)),
|
||||||
vm.NewBigIntegerItem(validators[i].Votes),
|
stackitem.NewBigInteger(validators[i].Votes),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return vm.NewArrayItem(arr)
|
return stackitem.NewArray(arr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValidatorsInternal returns a list of current validators.
|
// GetValidatorsInternal returns a list of current validators.
|
||||||
|
@ -430,7 +430,7 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (n *NEO) getValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
result, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
result, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -438,7 +438,7 @@ func (n *NEO) getValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem
|
||||||
return pubsToArray(result)
|
return pubsToArray(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
result, err := n.GetNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
result, err := n.GetNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -460,15 +460,15 @@ func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao
|
||||||
return pubs, nil
|
return pubs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pubsToArray(pubs keys.PublicKeys) vm.StackItem {
|
func pubsToArray(pubs keys.PublicKeys) stackitem.Item {
|
||||||
arr := make([]vm.StackItem, len(pubs))
|
arr := make([]stackitem.Item, len(pubs))
|
||||||
for i := range pubs {
|
for i := range pubs {
|
||||||
arr[i] = vm.NewByteArrayItem(pubs[i].Bytes())
|
arr[i] = stackitem.NewByteArray(pubs[i].Bytes())
|
||||||
}
|
}
|
||||||
return vm.NewArrayItem(arr)
|
return stackitem.NewArray(arr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPublicKey(s vm.StackItem) *keys.PublicKey {
|
func toPublicKey(s stackitem.Item) *keys.PublicKey {
|
||||||
buf, err := s.TryBytes()
|
buf, err := s.TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -7,11 +7,11 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// prefixAccount is the standard prefix used to store account data.
|
// prefixAccount is the standard prefix used to store account data.
|
||||||
|
@ -86,20 +86,20 @@ func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Name(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) Name(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
return vm.NewByteArrayItem([]byte(c.name))
|
return stackitem.NewByteArray([]byte(c.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) Symbol(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
return vm.NewByteArrayItem([]byte(c.symbol))
|
return stackitem.NewByteArray([]byte(c.symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
return vm.NewBigIntegerItem(big.NewInt(c.decimals))
|
return stackitem.NewBigInteger(big.NewInt(c.decimals))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
return vm.NewBigIntegerItem(c.getTotalSupply(ic))
|
return stackitem.NewBigInteger(c.getTotalSupply(ic))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
|
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
|
||||||
|
@ -107,37 +107,37 @@ func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
|
||||||
if si == nil {
|
if si == nil {
|
||||||
return big.NewInt(0)
|
return big.NewInt(0)
|
||||||
}
|
}
|
||||||
return emit.BytesToInt(si.Value)
|
return bigint.FromBytes(si.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error {
|
func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error {
|
||||||
si := &state.StorageItem{Value: emit.IntToBytes(supply)}
|
si := &state.StorageItem{Value: bigint.ToBytes(supply)}
|
||||||
return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si)
|
return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
from := toUint160(args[0])
|
from := toUint160(args[0])
|
||||||
to := toUint160(args[1])
|
to := toUint160(args[1])
|
||||||
amount := toBigInt(args[2])
|
amount := toBigInt(args[2])
|
||||||
err := c.transfer(ic, from, to, amount)
|
err := c.transfer(ic, from, to, amount)
|
||||||
return vm.NewBoolItem(err == nil)
|
return stackitem.NewBool(err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addrToStackItem(u *util.Uint160) vm.StackItem {
|
func addrToStackItem(u *util.Uint160) stackitem.Item {
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return vm.NullItem{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
return vm.NewByteArrayItem(u.BytesBE())
|
return stackitem.NewByteArray(u.BytesBE())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
|
func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
|
||||||
ne := state.NotificationEvent{
|
ne := state.NotificationEvent{
|
||||||
ScriptHash: c.Hash,
|
ScriptHash: c.Hash,
|
||||||
Item: vm.NewArrayItem([]vm.StackItem{
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
vm.NewByteArrayItem([]byte("Transfer")),
|
stackitem.NewByteArray([]byte("Transfer")),
|
||||||
addrToStackItem(from),
|
addrToStackItem(from),
|
||||||
addrToStackItem(to),
|
addrToStackItem(to),
|
||||||
vm.NewBigIntegerItem(amount),
|
stackitem.NewBigInteger(amount),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
ic.Notifications = append(ic.Notifications, ne)
|
ic.Notifications = append(ic.Notifications, ne)
|
||||||
|
@ -197,14 +197,14 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
h := toUint160(args[0])
|
h := toUint160(args[0])
|
||||||
bs, err := ic.DAO.GetNEP5Balances(h)
|
bs, err := ic.DAO.GetNEP5Balances(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
balance := bs.Trackers[c.Hash].Balance
|
balance := bs.Trackers[c.Hash].Balance
|
||||||
return vm.NewBigIntegerItem(big.NewInt(balance))
|
return stackitem.NewBigInteger(big.NewInt(balance))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||||
|
@ -269,7 +269,7 @@ func newMethodAndPrice(f interop.Method, price int64, flags smartcontract.CallFl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toBigInt(s vm.StackItem) *big.Int {
|
func toBigInt(s stackitem.Item) *big.Int {
|
||||||
bi, err := s.TryInteger()
|
bi, err := s.TryInteger()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -277,7 +277,7 @@ func toBigInt(s vm.StackItem) *big.Int {
|
||||||
return bi
|
return bi
|
||||||
}
|
}
|
||||||
|
|
||||||
func toUint160(s vm.StackItem) util.Uint160 {
|
func toUint160(s stackitem.Item) util.Uint160 {
|
||||||
buf, err := s.TryBytes()
|
buf, err := s.TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxValidatorsVoted limits the number of validators that one can vote for.
|
// MaxValidatorsVoted limits the number of validators that one can vote for.
|
||||||
|
@ -41,19 +42,25 @@ func (vc *ValidatorsCount) Bytes() []byte {
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
|
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteVarUint(uint64(MaxValidatorsVoted))
|
||||||
for i := range vc {
|
for i := range vc {
|
||||||
w.WriteVarBytes(emit.IntToBytes(&vc[i]))
|
w.WriteVarBytes(bigint.ToBytes(&vc[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
|
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
|
||||||
for i := range vc {
|
count := r.ReadVarUint()
|
||||||
|
if count < 0 || count > MaxValidatorsVoted {
|
||||||
|
r.Err = errors.New("invalid validators count")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
buf := r.ReadVarBytes()
|
buf := r.ReadVarBytes()
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vc[i] = *emit.BytesToInt(buf)
|
vc[i] = *bigint.FromBytes(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ func newTestNative() *testNative {
|
||||||
return tn
|
return tn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *testNative) sum(_ *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (tn *testNative) sum(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
s1, err := args[0].TryInteger()
|
s1, err := args[0].TryInteger()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -76,7 +76,7 @@ func (tn *testNative) sum(_ *interop.Context, args []vm.StackItem) vm.StackItem
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return vm.NewBigIntegerItem(s1.Add(s1, s2))
|
return stackitem.NewBigInteger(s1.Add(s1, s2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNativeContract_Invoke(t *testing.T) {
|
func TestNativeContract_Invoke(t *testing.T) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NEP5BalanceState represents balance state of a NEP5-token.
|
// NEP5BalanceState represents balance state of a NEP5-token.
|
||||||
|
@ -44,18 +44,27 @@ func (s *NEP5BalanceState) Bytes() []byte {
|
||||||
return w.Bytes()
|
return w.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *NEP5BalanceState) toStackItem() stackitem.Item {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{stackitem.NewBigInteger(&s.Balance)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NEP5BalanceState) fromStackItem(item stackitem.Item) {
|
||||||
|
s.Balance = *item.(*stackitem.Struct).Value().([]stackitem.Item)[0].Value().(*big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (s *NEP5BalanceState) EncodeBinary(w *io.BinWriter) {
|
func (s *NEP5BalanceState) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteVarBytes(emit.IntToBytes(&s.Balance))
|
si := s.toStackItem()
|
||||||
|
stackitem.EncodeBinaryStackItem(si, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) {
|
func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
buf := r.ReadVarBytes()
|
si := stackitem.DecodeBinaryStackItem(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Balance = *emit.BytesToInt(buf)
|
s.fromStackItem(si)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure.
|
// NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure.
|
||||||
|
@ -85,14 +94,37 @@ func (s *NEOBalanceState) Bytes() []byte {
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
|
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
|
||||||
s.NEP5BalanceState.EncodeBinary(w)
|
si := s.toStackItem()
|
||||||
w.WriteU32LE(s.BalanceHeight)
|
stackitem.EncodeBinaryStackItem(si, w)
|
||||||
w.WriteArray(s.Votes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
s.NEP5BalanceState.DecodeBinary(r)
|
si := stackitem.DecodeBinaryStackItem(r)
|
||||||
s.BalanceHeight = r.ReadU32LE()
|
if r.Err != nil {
|
||||||
r.ReadArray(&s.Votes)
|
return
|
||||||
|
}
|
||||||
|
s.fromStackItem(si)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NEOBalanceState) toStackItem() stackitem.Item {
|
||||||
|
result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct)
|
||||||
|
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
|
||||||
|
votes := make([]stackitem.Item, len(s.Votes))
|
||||||
|
for i, v := range s.Votes {
|
||||||
|
votes[i] = stackitem.NewByteArray(v.Bytes())
|
||||||
|
}
|
||||||
|
result.Append(stackitem.NewArray(votes))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NEOBalanceState) fromStackItem(item stackitem.Item) {
|
||||||
|
structItem := item.Value().([]stackitem.Item)
|
||||||
|
s.Balance = *structItem[0].Value().(*big.Int)
|
||||||
|
s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64())
|
||||||
|
votes := structItem[2].Value().([]stackitem.Item)
|
||||||
|
s.Votes = make([]*keys.PublicKey, len(votes))
|
||||||
|
for i, v := range votes {
|
||||||
|
s.Votes[i].DecodeBytes(v.Value().([]byte))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,14 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotificationEvent is a tuple of scripthash that emitted the StackItem as a
|
// NotificationEvent is a tuple of scripthash that emitted the Item as a
|
||||||
// notification and that item itself.
|
// notification and that item itself.
|
||||||
type NotificationEvent struct {
|
type NotificationEvent struct {
|
||||||
ScriptHash util.Uint160
|
ScriptHash util.Uint160
|
||||||
Item vm.StackItem
|
Item stackitem.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppExecResult represent the result of the script execution, gathering together
|
// AppExecResult represent the result of the script execution, gathering together
|
||||||
|
@ -29,13 +29,13 @@ type AppExecResult struct {
|
||||||
// EncodeBinary implements the Serializable interface.
|
// EncodeBinary implements the Serializable interface.
|
||||||
func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) {
|
func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) {
|
||||||
ne.ScriptHash.EncodeBinary(w)
|
ne.ScriptHash.EncodeBinary(w)
|
||||||
vm.EncodeBinaryStackItem(ne.Item, w)
|
stackitem.EncodeBinaryStackItem(ne.Item, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements the Serializable interface.
|
// DecodeBinary implements the Serializable interface.
|
||||||
func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
|
func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
|
||||||
ne.ScriptHash.DecodeBinary(r)
|
ne.ScriptHash.DecodeBinary(r)
|
||||||
ne.Item = vm.DecodeBinaryStackItem(r)
|
ne.Item = stackitem.DecodeBinaryStackItem(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements the Serializable interface.
|
// EncodeBinary implements the Serializable interface.
|
||||||
|
|
|
@ -6,13 +6,13 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEncodeDecodeNotificationEvent(t *testing.T) {
|
func TestEncodeDecodeNotificationEvent(t *testing.T) {
|
||||||
event := &NotificationEvent{
|
event := &NotificationEvent{
|
||||||
ScriptHash: random.Uint160(),
|
ScriptHash: random.Uint160(),
|
||||||
Item: vm.NewBoolItem(true),
|
Item: stackitem.NewBool(true),
|
||||||
}
|
}
|
||||||
|
|
||||||
testserdes.EncodeDecodeBinary(t, event, new(NotificationEvent))
|
testserdes.EncodeDecodeBinary(t, event, new(NotificationEvent))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package emit
|
package bigint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -9,9 +9,9 @@ import (
|
||||||
// wordSizeBytes is a size of a big.Word (uint) in bytes.`
|
// wordSizeBytes is a size of a big.Word (uint) in bytes.`
|
||||||
const wordSizeBytes = bits.UintSize / 8
|
const wordSizeBytes = bits.UintSize / 8
|
||||||
|
|
||||||
// BytesToInt converts data in little-endian format to
|
// FromBytes converts data in little-endian format to
|
||||||
// an integer.
|
// an integer.
|
||||||
func BytesToInt(data []byte) *big.Int {
|
func FromBytes(data []byte) *big.Int {
|
||||||
n := new(big.Int)
|
n := new(big.Int)
|
||||||
size := len(data)
|
size := len(data)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
|
@ -79,15 +79,17 @@ func getEffectiveSize(buf []byte, isNeg bool) int {
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntToBytes converts integer to a slice in little-endian format.
|
// ToBytes converts integer to a slice in little-endian format.
|
||||||
// Note: NEO3 serialization differs from default C# BigInteger.ToByteArray()
|
// Note: NEO3 serialization differs from default C# BigInteger.ToByteArray()
|
||||||
// when n == 0. For zero is equal to empty slice in NEO3.
|
// when n == 0. For zero is equal to empty slice in NEO3.
|
||||||
// https://github.com/neo-project/neo-vm/blob/master/src/neo-vm/Types/Integer.cs#L16
|
// https://github.com/neo-project/neo-vm/blob/master/src/neo-vm/Types/Integer.cs#L16
|
||||||
func IntToBytes(n *big.Int) []byte {
|
func ToBytes(n *big.Int) []byte {
|
||||||
return intToBytes(n, []byte{})
|
return ToPreallocatedBytes(n, []byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func intToBytes(n *big.Int, data []byte) []byte {
|
// ToPreallocatedBytes converts integer to a slice in little-endian format using given
|
||||||
|
// byte array for conversion result.
|
||||||
|
func ToPreallocatedBytes(n *big.Int, data []byte) []byte {
|
||||||
sign := n.Sign()
|
sign := n.Sign()
|
||||||
if sign == 0 {
|
if sign == 0 {
|
||||||
return data
|
return data
|
|
@ -1,4 +1,4 @@
|
||||||
package emit
|
package bigint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
@ -106,19 +106,19 @@ var testCases = []struct {
|
||||||
|
|
||||||
func TestIntToBytes(t *testing.T) {
|
func TestIntToBytes(t *testing.T) {
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
buf := IntToBytes(big.NewInt(tc.number))
|
buf := ToBytes(big.NewInt(tc.number))
|
||||||
assert.Equal(t, tc.buf, buf, "error while converting %d", tc.number)
|
assert.Equal(t, tc.buf, buf, "error while converting %d", tc.number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBytesToInt(t *testing.T) {
|
func TestBytesToInt(t *testing.T) {
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
num := BytesToInt(tc.buf)
|
num := FromBytes(tc.buf)
|
||||||
assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number)
|
assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("empty array", func(t *testing.T) {
|
t.Run("empty array", func(t *testing.T) {
|
||||||
require.EqualValues(t, 0, BytesToInt([]byte{}).Int64())
|
require.EqualValues(t, 0, FromBytes([]byte{}).Int64())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ func TestEquivalentRepresentations(t *testing.T) {
|
||||||
buf = append(buf, 0xFF, 0xFF, 0xFF)
|
buf = append(buf, 0xFF, 0xFF, 0xFF)
|
||||||
}
|
}
|
||||||
|
|
||||||
num := BytesToInt(buf)
|
num := FromBytes(buf)
|
||||||
assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number)
|
assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,16 +170,16 @@ func TestVeryBigInts(t *testing.T) {
|
||||||
num, ok := new(big.Int).SetString(tc.numStr, 10)
|
num, ok := new(big.Int).SetString(tc.numStr, 10)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
result := BytesToInt(tc.buf)
|
result := FromBytes(tc.buf)
|
||||||
assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr)
|
assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr)
|
||||||
assert.Equal(t, tc.buf, IntToBytes(result), "error while converting %s to bytes", tc.numStr)
|
assert.Equal(t, tc.buf, ToBytes(result), "error while converting %s to bytes", tc.numStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range stdlibCases {
|
for _, tc := range stdlibCases {
|
||||||
num, ok := new(big.Int).SetString(tc.numStr, 10)
|
num, ok := new(big.Int).SetString(tc.numStr, 10)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
result := BytesToInt(util.ArrayReverse(tc.buf))
|
result := FromBytes(util.ArrayReverse(tc.buf))
|
||||||
assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr)
|
assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -180,7 +181,7 @@ func topIntFromStack(st []smartcontract.Parameter) (int64, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, errors.New("invalid ByteArray item")
|
return 0, errors.New("invalid ByteArray item")
|
||||||
}
|
}
|
||||||
decimals = emit.BytesToInt(data).Int64()
|
decimals = bigint.FromBytes(data).Int64()
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("invalid stack item type: %s", typ)
|
return 0, fmt.Errorf("invalid stack item type: %s", typ)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApplicationLog wrapper used for the representation of the
|
// ApplicationLog wrapper used for the representation of the
|
||||||
|
@ -33,8 +33,8 @@ type NotificationEvent struct {
|
||||||
// StateEventToResultNotification converts state.NotificationEvent to
|
// StateEventToResultNotification converts state.NotificationEvent to
|
||||||
// result.NotificationEvent.
|
// result.NotificationEvent.
|
||||||
func StateEventToResultNotification(event state.NotificationEvent) NotificationEvent {
|
func StateEventToResultNotification(event state.NotificationEvent) NotificationEvent {
|
||||||
seen := make(map[vm.StackItem]bool)
|
seen := make(map[stackitem.Item]bool)
|
||||||
item := event.Item.ToContractParameter(seen)
|
item := smartcontract.ParameterFromStackItem(event.Item, seen)
|
||||||
return NotificationEvent{
|
return NotificationEvent{
|
||||||
Contract: event.ScriptHash,
|
Contract: event.ScriptHash,
|
||||||
Item: item,
|
Item: item,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network"
|
"github.com/nspcc-dev/neo-go/pkg/network"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
||||||
|
@ -29,7 +30,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -625,7 +625,7 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6
|
||||||
case smartcontract.IntegerType:
|
case smartcontract.IntegerType:
|
||||||
d = item.Value.(int64)
|
d = item.Value.(int64)
|
||||||
case smartcontract.ByteArrayType:
|
case smartcontract.ByteArrayType:
|
||||||
d = emit.BytesToInt(item.Value.([]byte)).Int64()
|
d = bigint.FromBytes(item.Value.([]byte)).Int64()
|
||||||
default:
|
default:
|
||||||
return 0, response.NewInternalServerError("invalid result", errors.New("not an integer"))
|
return 0, response.NewInternalServerError("invalid result", errors.New("not an integer"))
|
||||||
}
|
}
|
||||||
|
|
72
pkg/smartcontract/convertor.go
Normal file
72
pkg/smartcontract/convertor.go
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
79
pkg/smartcontract/convertor_test.go
Normal file
79
pkg/smartcontract/convertor_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"gopkg.in/abiosoft/ishell.v2"
|
"gopkg.in/abiosoft/ishell.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -276,7 +277,7 @@ func handleRun(c *ishell.Context) {
|
||||||
if len(c.Args) != 0 {
|
if len(c.Args) != 0 {
|
||||||
var (
|
var (
|
||||||
method []byte
|
method []byte
|
||||||
params []vm.StackItem
|
params []stackitem.Item
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
method = []byte(c.Args[0])
|
method = []byte(c.Args[0])
|
||||||
|
@ -406,8 +407,8 @@ func isMethodArg(s string) bool {
|
||||||
return len(strings.Split(s, ":")) == 1
|
return len(strings.Split(s, ":")) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseArgs(args []string) ([]vm.StackItem, error) {
|
func parseArgs(args []string) ([]stackitem.Item, error) {
|
||||||
items := make([]vm.StackItem, len(args))
|
items := make([]stackitem.Item, len(args))
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
var typ, value string
|
var typ, value string
|
||||||
typeAndVal := strings.Split(arg, ":")
|
typeAndVal := strings.Split(arg, ":")
|
||||||
|
@ -428,9 +429,9 @@ func parseArgs(args []string) ([]vm.StackItem, error) {
|
||||||
switch typ {
|
switch typ {
|
||||||
case boolType:
|
case boolType:
|
||||||
if value == boolFalse {
|
if value == boolFalse {
|
||||||
items[i] = vm.NewBoolItem(false)
|
items[i] = stackitem.NewBool(false)
|
||||||
} else if value == boolTrue {
|
} else if value == boolTrue {
|
||||||
items[i] = vm.NewBoolItem(true)
|
items[i] = stackitem.NewBool(true)
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("failed to parse bool parameter")
|
return nil, errors.New("failed to parse bool parameter")
|
||||||
}
|
}
|
||||||
|
@ -439,9 +440,9 @@ func parseArgs(args []string) ([]vm.StackItem, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
items[i] = vm.NewBigIntegerItem(big.NewInt(val))
|
items[i] = stackitem.NewBigInteger(big.NewInt(val))
|
||||||
case stringType:
|
case stringType:
|
||||||
items[i] = vm.NewByteArrayItem([]byte(value))
|
items[i] = stackitem.NewByteArray([]byte(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context represents the current execution context of the VM.
|
// Context represents the current execution context of the VM.
|
||||||
|
@ -170,50 +170,42 @@ func (c *Context) ScriptHash() util.Uint160 {
|
||||||
return c.scriptHash
|
return c.scriptHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value implements StackItem interface.
|
// Value implements stackitem.Item interface.
|
||||||
func (c *Context) Value() interface{} {
|
func (c *Context) Value() interface{} {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dup implements StackItem interface.
|
// Dup implements stackitem.Item interface.
|
||||||
func (c *Context) Dup() StackItem {
|
func (c *Context) Dup() stackitem.Item {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bool implements StackItem interface.
|
// Bool implements stackitem.Item interface.
|
||||||
func (c *Context) Bool() bool { panic("can't convert Context to Bool") }
|
func (c *Context) Bool() bool { panic("can't convert Context to Bool") }
|
||||||
|
|
||||||
// TryBytes implements StackItem interface.
|
// TryBytes implements stackitem.Item interface.
|
||||||
func (c *Context) TryBytes() ([]byte, error) {
|
func (c *Context) TryBytes() ([]byte, error) {
|
||||||
return nil, errors.New("can't convert Context to ByteArray")
|
return nil, errors.New("can't convert Context to ByteArray")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements StackItem interface.
|
// TryInteger implements stackitem.Item interface.
|
||||||
func (c *Context) TryInteger() (*big.Int, error) {
|
func (c *Context) TryInteger() (*big.Int, error) {
|
||||||
return nil, errors.New("can't convert Context to Integer")
|
return nil, errors.New("can't convert Context to Integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type implements StackItem interface.
|
// Type implements stackitem.Item interface.
|
||||||
func (c *Context) Type() StackItemType { panic("Context cannot appear on evaluation stack") }
|
func (c *Context) Type() stackitem.Type { panic("Context cannot appear on evaluation stack") }
|
||||||
|
|
||||||
// Convert implements StackItem interface.
|
// Convert implements stackitem.Item interface.
|
||||||
func (c *Context) Convert(_ StackItemType) (StackItem, error) {
|
func (c *Context) Convert(_ stackitem.Type) (stackitem.Item, error) {
|
||||||
panic("Context cannot be converted to anything")
|
panic("Context cannot be converted to anything")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements StackItem interface.
|
// Equals implements stackitem.Item interface.
|
||||||
func (c *Context) Equals(s StackItem) bool {
|
func (c *Context) Equals(s stackitem.Item) bool {
|
||||||
return c == s
|
return c == s
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToContractParameter implements StackItem interface.
|
|
||||||
func (c *Context) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
|
|
||||||
return smartcontract.Parameter{
|
|
||||||
Type: smartcontract.StringType,
|
|
||||||
Value: c.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) atBreakPoint() bool {
|
func (c *Context) atBreakPoint() bool {
|
||||||
for _, n := range c.breakPoints {
|
for _, n := range c.breakPoints {
|
||||||
if n == c.ip {
|
if n == c.ip {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package vm
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
@ -19,7 +20,7 @@ func getNumOfThingsFromInstr(instr opcode.Opcode, param []byte) (int, bool) {
|
||||||
case opcode.PUSH1 <= instr && instr <= opcode.PUSH16:
|
case opcode.PUSH1 <= instr && instr <= opcode.PUSH16:
|
||||||
nthings = int(instr-opcode.PUSH1) + 1
|
nthings = int(instr-opcode.PUSH1) + 1
|
||||||
case instr <= opcode.PUSHINT256:
|
case instr <= opcode.PUSHINT256:
|
||||||
n := emit.BytesToInt(param)
|
n := bigint.FromBytes(param)
|
||||||
if !n.IsInt64() || n.Int64() > MaxArraySize {
|
if !n.IsInt64() || n.Int64() > MaxArraySize {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,11 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Instruction emits a VM Instruction with data to the given buffer.
|
// Instruction emits a VM Instruction with data to the given buffer.
|
||||||
|
@ -31,7 +33,7 @@ func Bool(w *io.BinWriter, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Opcode(w, opcode.PUSHF)
|
Opcode(w, opcode.PUSHF)
|
||||||
Instruction(w, opcode.CONVERT, []byte{0x20}) // 0x20 for Boolean type
|
Instruction(w, opcode.CONVERT, []byte{byte(stackitem.BooleanT)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func padRight(s int, buf []byte) []byte {
|
func padRight(s int, buf []byte) []byte {
|
||||||
|
@ -54,7 +56,7 @@ func Int(w *io.BinWriter, i int64) {
|
||||||
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
||||||
Opcode(w, val)
|
Opcode(w, val)
|
||||||
default:
|
default:
|
||||||
buf := intToBytes(big.NewInt(i), make([]byte, 0, 32))
|
buf := bigint.ToPreallocatedBytes(big.NewInt(i), make([]byte, 0, 32))
|
||||||
// l != 0 becase of switch
|
// l != 0 becase of switch
|
||||||
padSize := byte(8 - bits.LeadingZeros8(byte(len(buf)-1)))
|
padSize := byte(8 - bits.LeadingZeros8(byte(len(buf)-1)))
|
||||||
Opcode(w, opcode.PUSHINT8+opcode.Opcode(padSize))
|
Opcode(w, opcode.PUSHINT8+opcode.Opcode(padSize))
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -49,7 +50,7 @@ func TestEmitInt(t *testing.T) {
|
||||||
result := buf.Bytes()
|
result := buf.Bytes()
|
||||||
assert.Equal(t, 3, len(result))
|
assert.Equal(t, 3, len(result))
|
||||||
assert.EqualValues(t, opcode.PUSHINT16, result[0])
|
assert.EqualValues(t, opcode.PUSHINT16, result[0])
|
||||||
assert.EqualValues(t, 300, BytesToInt(result[1:]).Int64())
|
assert.EqualValues(t, 300, bigint.FromBytes(result[1:]).Int64())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("3-byte int", func(t *testing.T) {
|
t.Run("3-byte int", func(t *testing.T) {
|
||||||
|
@ -58,7 +59,7 @@ func TestEmitInt(t *testing.T) {
|
||||||
result := buf.Bytes()
|
result := buf.Bytes()
|
||||||
assert.Equal(t, 5, len(result))
|
assert.Equal(t, 5, len(result))
|
||||||
assert.EqualValues(t, opcode.PUSHINT32, result[0])
|
assert.EqualValues(t, opcode.PUSHINT32, result[0])
|
||||||
assert.EqualValues(t, 1<<20, BytesToInt(result[1:]).Int64())
|
assert.EqualValues(t, 1<<20, bigint.FromBytes(result[1:]).Int64())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("4-byte int", func(t *testing.T) {
|
t.Run("4-byte int", func(t *testing.T) {
|
||||||
|
@ -67,7 +68,7 @@ func TestEmitInt(t *testing.T) {
|
||||||
result := buf.Bytes()
|
result := buf.Bytes()
|
||||||
assert.Equal(t, 5, len(result))
|
assert.Equal(t, 5, len(result))
|
||||||
assert.EqualValues(t, opcode.PUSHINT32, result[0])
|
assert.EqualValues(t, opcode.PUSHINT32, result[0])
|
||||||
assert.EqualValues(t, 1<<28, BytesToInt(result[1:]).Int64())
|
assert.EqualValues(t, 1<<28, bigint.FromBytes(result[1:]).Int64())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("negative 3-byte int with padding", func(t *testing.T) {
|
t.Run("negative 3-byte int with padding", func(t *testing.T) {
|
||||||
|
@ -77,7 +78,7 @@ func TestEmitInt(t *testing.T) {
|
||||||
result := buf.Bytes()
|
result := buf.Bytes()
|
||||||
assert.Equal(t, 5, len(result))
|
assert.Equal(t, 5, len(result))
|
||||||
assert.EqualValues(t, opcode.PUSHINT32, result[0])
|
assert.EqualValues(t, opcode.PUSHINT32, result[0])
|
||||||
assert.EqualValues(t, num, BytesToInt(result[1:]).Int64())
|
assert.EqualValues(t, num, bigint.FromBytes(result[1:]).Int64())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InteropFunc allows to hook into the VM.
|
// InteropFunc allows to hook into the VM.
|
||||||
|
@ -87,7 +88,7 @@ func runtimeNotify(vm *VM) error {
|
||||||
// RuntimeSerialize handles syscalls System.Runtime.Serialize and Neo.Runtime.Serialize.
|
// RuntimeSerialize handles syscalls System.Runtime.Serialize and Neo.Runtime.Serialize.
|
||||||
func RuntimeSerialize(vm *VM) error {
|
func RuntimeSerialize(vm *VM) error {
|
||||||
item := vm.Estack().Pop()
|
item := vm.Estack().Pop()
|
||||||
data, err := SerializeItem(item.value)
|
data, err := stackitem.SerializeItem(item.value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if len(data) > MaxItemSize {
|
} else if len(data) > MaxItemSize {
|
||||||
|
@ -103,7 +104,7 @@ func RuntimeSerialize(vm *VM) error {
|
||||||
func RuntimeDeserialize(vm *VM) error {
|
func RuntimeDeserialize(vm *VM) error {
|
||||||
data := vm.Estack().Pop().Bytes()
|
data := vm.Estack().Pop().Bytes()
|
||||||
|
|
||||||
item, err := DeserializeItem(data)
|
item, err := stackitem.DeserializeItem(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -124,7 +125,7 @@ func init() {
|
||||||
func EnumeratorCreate(v *VM) error {
|
func EnumeratorCreate(v *VM) error {
|
||||||
data := v.Estack().Pop().Array()
|
data := v.Estack().Pop().Array()
|
||||||
v.Estack().Push(&Element{
|
v.Estack().Push(&Element{
|
||||||
value: NewInteropItem(&arrayWrapper{
|
value: stackitem.NewInterop(&arrayWrapper{
|
||||||
index: -1,
|
index: -1,
|
||||||
value: data,
|
value: data,
|
||||||
}),
|
}),
|
||||||
|
@ -136,7 +137,7 @@ func EnumeratorCreate(v *VM) error {
|
||||||
// EnumeratorNext handles syscall Neo.Enumerator.Next.
|
// EnumeratorNext handles syscall Neo.Enumerator.Next.
|
||||||
func EnumeratorNext(v *VM) error {
|
func EnumeratorNext(v *VM) error {
|
||||||
iop := v.Estack().Pop().Interop()
|
iop := v.Estack().Pop().Interop()
|
||||||
arr := iop.value.(enumerator)
|
arr := iop.Value().(enumerator)
|
||||||
v.Estack().PushVal(arr.Next())
|
v.Estack().PushVal(arr.Next())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -145,7 +146,7 @@ func EnumeratorNext(v *VM) error {
|
||||||
// EnumeratorValue handles syscall Neo.Enumerator.Value.
|
// EnumeratorValue handles syscall Neo.Enumerator.Value.
|
||||||
func EnumeratorValue(v *VM) error {
|
func EnumeratorValue(v *VM) error {
|
||||||
iop := v.Estack().Pop().Interop()
|
iop := v.Estack().Pop().Interop()
|
||||||
arr := iop.value.(enumerator)
|
arr := iop.Value().(enumerator)
|
||||||
v.Estack().Push(&Element{value: arr.Value()})
|
v.Estack().Push(&Element{value: arr.Value()})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -154,12 +155,12 @@ func EnumeratorValue(v *VM) error {
|
||||||
// EnumeratorConcat handles syscall Neo.Enumerator.Concat.
|
// EnumeratorConcat handles syscall Neo.Enumerator.Concat.
|
||||||
func EnumeratorConcat(v *VM) error {
|
func EnumeratorConcat(v *VM) error {
|
||||||
iop1 := v.Estack().Pop().Interop()
|
iop1 := v.Estack().Pop().Interop()
|
||||||
arr1 := iop1.value.(enumerator)
|
arr1 := iop1.Value().(enumerator)
|
||||||
iop2 := v.Estack().Pop().Interop()
|
iop2 := v.Estack().Pop().Interop()
|
||||||
arr2 := iop2.value.(enumerator)
|
arr2 := iop2.Value().(enumerator)
|
||||||
|
|
||||||
v.Estack().Push(&Element{
|
v.Estack().Push(&Element{
|
||||||
value: NewInteropItem(&concatEnum{
|
value: stackitem.NewInterop(&concatEnum{
|
||||||
current: arr1,
|
current: arr1,
|
||||||
second: arr2,
|
second: arr2,
|
||||||
}),
|
}),
|
||||||
|
@ -171,14 +172,14 @@ func EnumeratorConcat(v *VM) error {
|
||||||
// IteratorCreate handles syscall Neo.Iterator.Create.
|
// IteratorCreate handles syscall Neo.Iterator.Create.
|
||||||
func IteratorCreate(v *VM) error {
|
func IteratorCreate(v *VM) error {
|
||||||
data := v.Estack().Pop()
|
data := v.Estack().Pop()
|
||||||
var item StackItem
|
var item stackitem.Item
|
||||||
switch t := data.value.(type) {
|
switch t := data.value.(type) {
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
item = NewInteropItem(&arrayWrapper{
|
item = stackitem.NewInterop(&arrayWrapper{
|
||||||
index: -1,
|
index: -1,
|
||||||
value: t.Value().([]StackItem),
|
value: t.Value().([]stackitem.Item),
|
||||||
})
|
})
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
item = NewMapIterator(t)
|
item = NewMapIterator(t)
|
||||||
default:
|
default:
|
||||||
return errors.New("non-iterable type")
|
return errors.New("non-iterable type")
|
||||||
|
@ -189,21 +190,21 @@ func IteratorCreate(v *VM) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMapIterator returns new interop item containing iterator over m.
|
// NewMapIterator returns new interop item containing iterator over m.
|
||||||
func NewMapIterator(m *MapItem) *InteropItem {
|
func NewMapIterator(m *stackitem.Map) *stackitem.Interop {
|
||||||
return NewInteropItem(&mapWrapper{
|
return stackitem.NewInterop(&mapWrapper{
|
||||||
index: -1,
|
index: -1,
|
||||||
m: m.value,
|
m: m.Value().([]stackitem.MapElement),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// IteratorConcat handles syscall Neo.Iterator.Concat.
|
// IteratorConcat handles syscall Neo.Iterator.Concat.
|
||||||
func IteratorConcat(v *VM) error {
|
func IteratorConcat(v *VM) error {
|
||||||
iop1 := v.Estack().Pop().Interop()
|
iop1 := v.Estack().Pop().Interop()
|
||||||
iter1 := iop1.value.(iterator)
|
iter1 := iop1.Value().(iterator)
|
||||||
iop2 := v.Estack().Pop().Interop()
|
iop2 := v.Estack().Pop().Interop()
|
||||||
iter2 := iop2.value.(iterator)
|
iter2 := iop2.Value().(iterator)
|
||||||
|
|
||||||
v.Estack().Push(&Element{value: NewInteropItem(
|
v.Estack().Push(&Element{value: stackitem.NewInterop(
|
||||||
&concatIter{
|
&concatIter{
|
||||||
current: iter1,
|
current: iter1,
|
||||||
second: iter2,
|
second: iter2,
|
||||||
|
@ -216,7 +217,7 @@ func IteratorConcat(v *VM) error {
|
||||||
// IteratorKey handles syscall Neo.Iterator.Key.
|
// IteratorKey handles syscall Neo.Iterator.Key.
|
||||||
func IteratorKey(v *VM) error {
|
func IteratorKey(v *VM) error {
|
||||||
iop := v.estack.Pop().Interop()
|
iop := v.estack.Pop().Interop()
|
||||||
iter := iop.value.(iterator)
|
iter := iop.Value().(iterator)
|
||||||
v.Estack().Push(&Element{value: iter.Key()})
|
v.Estack().Push(&Element{value: iter.Key()})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -225,8 +226,8 @@ func IteratorKey(v *VM) error {
|
||||||
// IteratorKeys handles syscall Neo.Iterator.Keys.
|
// IteratorKeys handles syscall Neo.Iterator.Keys.
|
||||||
func IteratorKeys(v *VM) error {
|
func IteratorKeys(v *VM) error {
|
||||||
iop := v.estack.Pop().Interop()
|
iop := v.estack.Pop().Interop()
|
||||||
iter := iop.value.(iterator)
|
iter := iop.Value().(iterator)
|
||||||
v.Estack().Push(&Element{value: NewInteropItem(
|
v.Estack().Push(&Element{value: stackitem.NewInterop(
|
||||||
&keysWrapper{iter},
|
&keysWrapper{iter},
|
||||||
)})
|
)})
|
||||||
|
|
||||||
|
@ -236,8 +237,8 @@ func IteratorKeys(v *VM) error {
|
||||||
// IteratorValues handles syscall Neo.Iterator.Values.
|
// IteratorValues handles syscall Neo.Iterator.Values.
|
||||||
func IteratorValues(v *VM) error {
|
func IteratorValues(v *VM) error {
|
||||||
iop := v.estack.Pop().Interop()
|
iop := v.estack.Pop().Interop()
|
||||||
iter := iop.value.(iterator)
|
iter := iop.Value().(iterator)
|
||||||
v.Estack().Push(&Element{value: NewInteropItem(
|
v.Estack().Push(&Element{value: stackitem.NewInterop(
|
||||||
&valuesWrapper{iter},
|
&valuesWrapper{iter},
|
||||||
)})
|
)})
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
enumerator interface {
|
enumerator interface {
|
||||||
Next() bool
|
Next() bool
|
||||||
Value() StackItem
|
Value() stackitem.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayWrapper struct {
|
arrayWrapper struct {
|
||||||
index int
|
index int
|
||||||
value []StackItem
|
value []stackitem.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
concatEnum struct {
|
concatEnum struct {
|
||||||
|
@ -20,12 +24,12 @@ type (
|
||||||
type (
|
type (
|
||||||
iterator interface {
|
iterator interface {
|
||||||
enumerator
|
enumerator
|
||||||
Key() StackItem
|
Key() stackitem.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
mapWrapper struct {
|
mapWrapper struct {
|
||||||
index int
|
index int
|
||||||
m []MapElement
|
m []stackitem.MapElement
|
||||||
}
|
}
|
||||||
|
|
||||||
concatIter struct {
|
concatIter struct {
|
||||||
|
@ -51,12 +55,12 @@ func (a *arrayWrapper) Next() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *arrayWrapper) Value() StackItem {
|
func (a *arrayWrapper) Value() stackitem.Item {
|
||||||
return a.value[a.index]
|
return a.value[a.index]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *arrayWrapper) Key() StackItem {
|
func (a *arrayWrapper) Key() stackitem.Item {
|
||||||
return makeStackItem(a.index)
|
return stackitem.Make(a.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *concatEnum) Next() bool {
|
func (c *concatEnum) Next() bool {
|
||||||
|
@ -68,7 +72,7 @@ func (c *concatEnum) Next() bool {
|
||||||
return c.current.Next()
|
return c.current.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *concatEnum) Value() StackItem {
|
func (c *concatEnum) Value() stackitem.Item {
|
||||||
return c.current.Value()
|
return c.current.Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,11 +85,11 @@ func (i *concatIter) Next() bool {
|
||||||
return i.second.Next()
|
return i.second.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *concatIter) Value() StackItem {
|
func (i *concatIter) Value() stackitem.Item {
|
||||||
return i.current.Value()
|
return i.current.Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *concatIter) Key() StackItem {
|
func (i *concatIter) Key() stackitem.Item {
|
||||||
return i.current.Key()
|
return i.current.Key()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,11 +102,11 @@ func (m *mapWrapper) Next() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mapWrapper) Value() StackItem {
|
func (m *mapWrapper) Value() stackitem.Item {
|
||||||
return m.m[m.index].Value
|
return m.m[m.index].Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mapWrapper) Key() StackItem {
|
func (m *mapWrapper) Key() stackitem.Item {
|
||||||
return m.m[m.index].Key
|
return m.m[m.index].Key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +114,7 @@ func (e *keysWrapper) Next() bool {
|
||||||
return e.iter.Next()
|
return e.iter.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *keysWrapper) Value() StackItem {
|
func (e *keysWrapper) Value() stackitem.Item {
|
||||||
return e.iter.Key()
|
return e.iter.Key()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +122,6 @@ func (e *valuesWrapper) Next() bool {
|
||||||
return e.iter.Next()
|
return e.iter.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *valuesWrapper) Value() StackItem {
|
func (e *valuesWrapper) Value() stackitem.Item {
|
||||||
return e.iter.Value()
|
return e.iter.Value()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ func TestUT(t *testing.T) {
|
||||||
func getTestingInterop(id uint32) *InteropFuncPrice {
|
func getTestingInterop(id uint32) *InteropFuncPrice {
|
||||||
if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) {
|
if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) {
|
||||||
return &InteropFuncPrice{InteropFunc(func(v *VM) error {
|
return &InteropFuncPrice{InteropFunc(func(v *VM) error {
|
||||||
v.estack.PushVal(&InteropItem{new(int)})
|
v.estack.PushVal(stackitem.NewInterop(new(int)))
|
||||||
return nil
|
return nil
|
||||||
}), 0}
|
}), 0}
|
||||||
}
|
}
|
||||||
|
@ -174,17 +175,17 @@ func testFile(t *testing.T, filename string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareItems(t *testing.T, a, b StackItem) {
|
func compareItems(t *testing.T, a, b stackitem.Item) {
|
||||||
switch si := a.(type) {
|
switch si := a.(type) {
|
||||||
case *BigIntegerItem:
|
case *stackitem.BigInteger:
|
||||||
val := si.value.Int64()
|
val := si.Value().(*big.Int).Int64()
|
||||||
switch ac := b.(type) {
|
switch ac := b.(type) {
|
||||||
case *BigIntegerItem:
|
case *stackitem.BigInteger:
|
||||||
require.Equal(t, val, ac.value.Int64())
|
require.Equal(t, val, ac.Value().(*big.Int).Int64())
|
||||||
case *ByteArrayItem:
|
case *stackitem.ByteArray:
|
||||||
require.Equal(t, val, emit.BytesToInt(ac.value).Int64())
|
require.Equal(t, val, bigint.FromBytes(ac.Value().([]byte)).Int64())
|
||||||
case *BoolItem:
|
case *stackitem.Bool:
|
||||||
if ac.value {
|
if ac.Value().(bool) {
|
||||||
require.Equal(t, val, int64(1))
|
require.Equal(t, val, int64(1))
|
||||||
} else {
|
} else {
|
||||||
require.Equal(t, val, int64(0))
|
require.Equal(t, val, int64(0))
|
||||||
|
@ -192,17 +193,17 @@ func compareItems(t *testing.T, a, b StackItem) {
|
||||||
default:
|
default:
|
||||||
require.Fail(t, "wrong type")
|
require.Fail(t, "wrong type")
|
||||||
}
|
}
|
||||||
case *PointerItem:
|
case *stackitem.Pointer:
|
||||||
p, ok := b.(*PointerItem)
|
p, ok := b.(*stackitem.Pointer)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, si.pos, p.pos) // there no script in test files
|
require.Equal(t, si.Position(), p.Position()) // there no script in test files
|
||||||
default:
|
default:
|
||||||
require.Equal(t, a, b)
|
require.Equal(t, a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) {
|
func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) {
|
||||||
compareItemArrays(t, expected, actual.Len(), func(i int) StackItem { return actual.Peek(i).Item() })
|
compareItemArrays(t, expected, actual.Len(), func(i int) stackitem.Item { return actual.Peek(i).Item() })
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
|
func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
|
||||||
|
@ -213,7 +214,7 @@ func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
|
||||||
compareItemArrays(t, expected, actual.Size(), actual.Get)
|
compareItemArrays(t, expected, actual.Size(), actual.Get)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem func(i int) StackItem) {
|
func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem func(i int) stackitem.Item) {
|
||||||
if expected == nil {
|
if expected == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -224,57 +225,47 @@ func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem fu
|
||||||
require.NotNil(t, it)
|
require.NotNil(t, it)
|
||||||
|
|
||||||
if item.Type == typeInterop {
|
if item.Type == typeInterop {
|
||||||
require.IsType(t, (*InteropItem)(nil), it)
|
require.IsType(t, (*stackitem.Interop)(nil), it)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
compareItems(t, item.toStackItem(), it)
|
compareItems(t, item.toStackItem(), it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *vmUTStackItem) toStackItem() StackItem {
|
func (v *vmUTStackItem) toStackItem() stackitem.Item {
|
||||||
switch v.Type.toLower() {
|
switch v.Type.toLower() {
|
||||||
case typeArray:
|
case typeArray:
|
||||||
items := v.Value.([]vmUTStackItem)
|
items := v.Value.([]vmUTStackItem)
|
||||||
result := make([]StackItem, len(items))
|
result := make([]stackitem.Item, len(items))
|
||||||
for i := range items {
|
for i := range items {
|
||||||
result[i] = items[i].toStackItem()
|
result[i] = items[i].toStackItem()
|
||||||
}
|
}
|
||||||
return &ArrayItem{
|
return stackitem.NewArray(result)
|
||||||
value: result,
|
|
||||||
}
|
|
||||||
case typeString:
|
case typeString:
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
case typeMap:
|
case typeMap:
|
||||||
return v.Value.(*MapItem)
|
return v.Value.(*stackitem.Map)
|
||||||
case typeInterop:
|
case typeInterop:
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
case typeByteString:
|
case typeByteString:
|
||||||
return &ByteArrayItem{
|
return stackitem.NewByteArray(v.Value.([]byte))
|
||||||
v.Value.([]byte),
|
|
||||||
}
|
|
||||||
case typeBuffer:
|
case typeBuffer:
|
||||||
return &BufferItem{v.Value.([]byte)}
|
return stackitem.NewBuffer(v.Value.([]byte))
|
||||||
case typePointer:
|
case typePointer:
|
||||||
return NewPointerItem(v.Value.(int), nil)
|
return stackitem.NewPointer(v.Value.(int), nil)
|
||||||
case typeNull:
|
case typeNull:
|
||||||
return NullItem{}
|
return stackitem.Null{}
|
||||||
case typeBoolean:
|
case typeBoolean:
|
||||||
return &BoolItem{
|
return stackitem.NewBool(v.Value.(bool))
|
||||||
v.Value.(bool),
|
|
||||||
}
|
|
||||||
case typeInteger:
|
case typeInteger:
|
||||||
return &BigIntegerItem{
|
return stackitem.NewBigInteger(v.Value.(*big.Int))
|
||||||
value: v.Value.(*big.Int),
|
|
||||||
}
|
|
||||||
case typeStruct:
|
case typeStruct:
|
||||||
items := v.Value.([]vmUTStackItem)
|
items := v.Value.([]vmUTStackItem)
|
||||||
result := make([]StackItem, len(items))
|
result := make([]stackitem.Item, len(items))
|
||||||
for i := range items {
|
for i := range items {
|
||||||
result[i] = items[i].toStackItem()
|
result[i] = items[i].toStackItem()
|
||||||
}
|
}
|
||||||
return &StructItem{
|
return stackitem.NewStruct(result)
|
||||||
value: result,
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid type: %s", v.Type))
|
panic(fmt.Sprintf("invalid type: %s", v.Type))
|
||||||
}
|
}
|
||||||
|
@ -303,10 +294,10 @@ func execStep(t *testing.T, v *VM, step vmUTStep) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsonStringToInteger(s string) StackItem {
|
func jsonStringToInteger(s string) stackitem.Item {
|
||||||
b, err := decodeHex(s)
|
b, err := decodeHex(s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return NewBigIntegerItem(new(big.Int).SetBytes(b))
|
return stackitem.NewBigInteger(new(big.Int).SetBytes(b))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -418,7 +409,7 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
|
||||||
return fmt.Errorf("invalid map start")
|
return fmt.Errorf("invalid map start")
|
||||||
}
|
}
|
||||||
|
|
||||||
result := NewMapItem()
|
result := stackitem.NewMap()
|
||||||
for {
|
for {
|
||||||
tok, err := d.Token()
|
tok, err := d.Token()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -438,7 +429,7 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
item := jsonStringToInteger(key)
|
item := jsonStringToInteger(key)
|
||||||
if item == nil {
|
if item == nil {
|
||||||
return fmt.Errorf("can't unmarshal StackItem %s", key)
|
return fmt.Errorf("can't unmarshal Item %s", key)
|
||||||
}
|
}
|
||||||
result.Add(item, it.toStackItem())
|
result.Add(item, it.toStackItem())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,50 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
// refCounter represents reference counter for the VM.
|
// refCounter represents reference counter for the VM.
|
||||||
type refCounter struct {
|
type refCounter struct {
|
||||||
items map[StackItem]int
|
items map[stackitem.Item]int
|
||||||
size int
|
size int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRefCounter() *refCounter {
|
func newRefCounter() *refCounter {
|
||||||
return &refCounter{
|
return &refCounter{
|
||||||
items: make(map[StackItem]int),
|
items: make(map[stackitem.Item]int),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds an item to the reference counter.
|
// Add adds an item to the reference counter.
|
||||||
func (r *refCounter) Add(item StackItem) {
|
func (r *refCounter) Add(item stackitem.Item) {
|
||||||
r.size++
|
r.size++
|
||||||
|
|
||||||
switch item.(type) {
|
switch item.(type) {
|
||||||
case *ArrayItem, *StructItem, *MapItem:
|
case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
|
||||||
if r.items[item]++; r.items[item] > 1 {
|
if r.items[item]++; r.items[item] > 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := item.(type) {
|
switch t := item.(type) {
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
for _, it := range item.Value().([]StackItem) {
|
for _, it := range item.Value().([]stackitem.Item) {
|
||||||
r.Add(it)
|
r.Add(it)
|
||||||
}
|
}
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
for i := range t.value {
|
for i := range t.Value().([]stackitem.MapElement) {
|
||||||
r.Add(t.value[i].Value)
|
r.Add(t.Value().([]stackitem.MapElement)[i].Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes item from the reference counter.
|
// Remove removes item from the reference counter.
|
||||||
func (r *refCounter) Remove(item StackItem) {
|
func (r *refCounter) Remove(item stackitem.Item) {
|
||||||
r.size--
|
r.size--
|
||||||
|
|
||||||
switch item.(type) {
|
switch item.(type) {
|
||||||
case *ArrayItem, *StructItem, *MapItem:
|
case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
|
||||||
if r.items[item] > 1 {
|
if r.items[item] > 1 {
|
||||||
r.items[item]--
|
r.items[item]--
|
||||||
return
|
return
|
||||||
|
@ -49,13 +53,13 @@ func (r *refCounter) Remove(item StackItem) {
|
||||||
delete(r.items, item)
|
delete(r.items, item)
|
||||||
|
|
||||||
switch t := item.(type) {
|
switch t := item.(type) {
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
for _, it := range item.Value().([]StackItem) {
|
for _, it := range item.Value().([]stackitem.Item) {
|
||||||
r.Remove(it)
|
r.Remove(it)
|
||||||
}
|
}
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
for i := range t.value {
|
for i := range t.Value().([]stackitem.MapElement) {
|
||||||
r.Remove(t.value[i].Value)
|
r.Remove(t.Value().([]stackitem.MapElement)[i].Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package vm
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,13 +12,13 @@ func TestRefCounter_Add(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, 0, r.size)
|
require.Equal(t, 0, r.size)
|
||||||
|
|
||||||
r.Add(NullItem{})
|
r.Add(stackitem.Null{})
|
||||||
require.Equal(t, 1, r.size)
|
require.Equal(t, 1, r.size)
|
||||||
|
|
||||||
r.Add(NullItem{})
|
r.Add(stackitem.Null{})
|
||||||
require.Equal(t, 2, r.size) // count scalar items twice
|
require.Equal(t, 2, r.size) // count scalar items twice
|
||||||
|
|
||||||
arr := NewArrayItem([]StackItem{NewByteArrayItem([]byte{1}), NewBoolItem(false)})
|
arr := stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1}), stackitem.NewBool(false)})
|
||||||
r.Add(arr)
|
r.Add(arr)
|
||||||
require.Equal(t, 5, r.size) // array + 2 elements
|
require.Equal(t, 5, r.size) // array + 2 elements
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,19 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
// Slot is a fixed-size slice of stack items.
|
// Slot is a fixed-size slice of stack items.
|
||||||
type Slot struct {
|
type Slot struct {
|
||||||
storage []StackItem
|
storage []stackitem.Item
|
||||||
refs *refCounter
|
refs *refCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSlot returns new slot of n items.
|
// newSlot returns new slot of n items.
|
||||||
func newSlot(n int, refs *refCounter) *Slot {
|
func newSlot(n int, refs *refCounter) *Slot {
|
||||||
return &Slot{
|
return &Slot{
|
||||||
storage: make([]StackItem, n),
|
storage: make([]stackitem.Item, n),
|
||||||
refs: refs,
|
refs: refs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +23,7 @@ func (v *VM) newSlot(n int) *Slot {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets i-th storage slot.
|
// Set sets i-th storage slot.
|
||||||
func (s *Slot) Set(i int, item StackItem) {
|
func (s *Slot) Set(i int, item stackitem.Item) {
|
||||||
if s.storage[i] == item {
|
if s.storage[i] == item {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -32,11 +36,11 @@ func (s *Slot) Set(i int, item StackItem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns item contained in i-th slot.
|
// Get returns item contained in i-th slot.
|
||||||
func (s *Slot) Get(i int) StackItem {
|
func (s *Slot) Get(i int) stackitem.Item {
|
||||||
if item := s.storage[i]; item != nil {
|
if item := s.storage[i]; item != nil {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
return NullItem{}
|
return stackitem.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns slot size.
|
// Size returns slot size.
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,10 +13,10 @@ func TestSlot_Get(t *testing.T) {
|
||||||
require.NotNil(t, s)
|
require.NotNil(t, s)
|
||||||
require.Equal(t, 3, s.Size())
|
require.Equal(t, 3, s.Size())
|
||||||
|
|
||||||
// NullItem is the default
|
// Null is the default
|
||||||
item := s.Get(2)
|
item := s.Get(2)
|
||||||
require.Equal(t, NullItem{}, item)
|
require.Equal(t, stackitem.Null{}, item)
|
||||||
|
|
||||||
s.Set(1, NewBigIntegerItem(big.NewInt(42)))
|
s.Set(1, stackitem.NewBigInteger(big.NewInt(42)))
|
||||||
require.Equal(t, NewBigIntegerItem(big.NewInt(42)), s.Get(1))
|
require.Equal(t, stackitem.NewBigInteger(big.NewInt(42)), s.Get(1))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stack implementation for the neo-go virtual machine. The stack implements
|
// Stack implementation for the neo-go virtual machine. The stack implements
|
||||||
|
@ -29,9 +30,9 @@ import (
|
||||||
// [ 0 ]
|
// [ 0 ]
|
||||||
|
|
||||||
// Element represents an element in the double linked list (the stack),
|
// Element represents an element in the double linked list (the stack),
|
||||||
// which will hold the underlying StackItem.
|
// which will hold the underlying stackitem.Item.
|
||||||
type Element struct {
|
type Element struct {
|
||||||
value StackItem
|
value stackitem.Item
|
||||||
next, prev *Element
|
next, prev *Element
|
||||||
stack *Stack
|
stack *Stack
|
||||||
}
|
}
|
||||||
|
@ -40,7 +41,7 @@ type Element struct {
|
||||||
// to the corresponding type.
|
// to the corresponding type.
|
||||||
func NewElement(v interface{}) *Element {
|
func NewElement(v interface{}) *Element {
|
||||||
return &Element{
|
return &Element{
|
||||||
value: makeStackItem(v),
|
value: stackitem.Make(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,12 +61,12 @@ func (e *Element) Prev() *Element {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item returns StackItem contained in the element.
|
// Item returns Item contained in the element.
|
||||||
func (e *Element) Item() StackItem {
|
func (e *Element) Item() stackitem.Item {
|
||||||
return e.value
|
return e.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns value of the StackItem contained in the element.
|
// Value returns value of the Item contained in the element.
|
||||||
func (e *Element) Value() interface{} {
|
func (e *Element) Value() interface{} {
|
||||||
return e.value.Value()
|
return e.value.Value()
|
||||||
}
|
}
|
||||||
|
@ -98,12 +99,12 @@ func (e *Element) Bytes() []byte {
|
||||||
// Array attempts to get the underlying value of the element as an array of
|
// Array attempts to get the underlying value of the element as an array of
|
||||||
// other items. Will panic if the item type is different which will be caught
|
// other items. Will panic if the item type is different which will be caught
|
||||||
// by the VM.
|
// by the VM.
|
||||||
func (e *Element) Array() []StackItem {
|
func (e *Element) Array() []stackitem.Item {
|
||||||
switch t := e.value.(type) {
|
switch t := e.value.(type) {
|
||||||
case *ArrayItem:
|
case *stackitem.Array:
|
||||||
return t.value
|
return t.Value().([]stackitem.Item)
|
||||||
case *StructItem:
|
case *stackitem.Struct:
|
||||||
return t.value
|
return t.Value().([]stackitem.Item)
|
||||||
default:
|
default:
|
||||||
panic("element is not an array")
|
panic("element is not an array")
|
||||||
}
|
}
|
||||||
|
@ -111,9 +112,9 @@ func (e *Element) Array() []StackItem {
|
||||||
|
|
||||||
// Interop attempts to get the underlying value of the element
|
// Interop attempts to get the underlying value of the element
|
||||||
// as an interop item.
|
// as an interop item.
|
||||||
func (e *Element) Interop() *InteropItem {
|
func (e *Element) Interop() *stackitem.Interop {
|
||||||
switch t := e.value.(type) {
|
switch t := e.value.(type) {
|
||||||
case *InteropItem:
|
case *stackitem.Interop:
|
||||||
return t
|
return t
|
||||||
default:
|
default:
|
||||||
panic("element is not an interop")
|
panic("element is not an interop")
|
||||||
|
@ -190,7 +191,7 @@ func (s *Stack) Push(e *Element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushVal pushes the given value on the stack. It will infer the
|
// PushVal pushes the given value on the stack. It will infer the
|
||||||
// underlying StackItem to its corresponding type.
|
// underlying Item to its corresponding type.
|
||||||
func (s *Stack) PushVal(v interface{}) {
|
func (s *Stack) PushVal(v interface{}) {
|
||||||
s.Push(NewElement(v))
|
s.Push(NewElement(v))
|
||||||
}
|
}
|
||||||
|
@ -369,7 +370,7 @@ func (s *Stack) PopSigElements() ([][]byte, error) {
|
||||||
return nil, fmt.Errorf("nothing on the stack")
|
return nil, fmt.Errorf("nothing on the stack")
|
||||||
}
|
}
|
||||||
switch item.value.(type) {
|
switch item.value.(type) {
|
||||||
case *ArrayItem:
|
case *stackitem.Array:
|
||||||
num = len(item.Array())
|
num = len(item.Array())
|
||||||
if num < 1 {
|
if num < 1 {
|
||||||
return nil, fmt.Errorf("less than one element in the array")
|
return nil, fmt.Errorf("less than one element in the array")
|
||||||
|
@ -400,8 +401,8 @@ func (s *Stack) ToContractParameters() []smartcontract.Parameter {
|
||||||
items := make([]smartcontract.Parameter, 0, s.Len())
|
items := make([]smartcontract.Parameter, 0, s.Len())
|
||||||
s.IterBack(func(e *Element) {
|
s.IterBack(func(e *Element) {
|
||||||
// Each item is independent.
|
// Each item is independent.
|
||||||
seen := make(map[StackItem]bool)
|
seen := make(map[stackitem.Item]bool)
|
||||||
items = append(items, e.value.ToContractParameter(seen))
|
items = append(items, smartcontract.ParameterFromStackItem(e.value, seen))
|
||||||
})
|
})
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
1034
pkg/vm/stack_item.go
1034
pkg/vm/stack_item.go
File diff suppressed because it is too large
Load diff
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -231,9 +232,9 @@ func TestPushVal(t *testing.T) {
|
||||||
assert.Equal(t, true, elem.Bool())
|
assert.Equal(t, true, elem.Bool())
|
||||||
|
|
||||||
// array
|
// array
|
||||||
s.PushVal([]StackItem{&BoolItem{true}, &BoolItem{false}, &BoolItem{true}})
|
s.PushVal([]stackitem.Item{stackitem.NewBool(true), stackitem.NewBool(false), stackitem.NewBool(true)})
|
||||||
elem = s.Pop()
|
elem = s.Pop()
|
||||||
assert.IsType(t, elem.value, &ArrayItem{})
|
assert.IsType(t, elem.value, &stackitem.Array{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSwapElemValues(t *testing.T) {
|
func TestSwapElemValues(t *testing.T) {
|
||||||
|
@ -320,17 +321,17 @@ func TestPopSigElements(t *testing.T) {
|
||||||
_, err := s.PopSigElements()
|
_, err := s.PopSigElements()
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
s.PushVal([]StackItem{})
|
s.PushVal([]stackitem.Item{})
|
||||||
_, err = s.PopSigElements()
|
_, err = s.PopSigElements()
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
s.PushVal([]StackItem{NewBoolItem(false)})
|
s.PushVal([]stackitem.Item{stackitem.NewBool(false)})
|
||||||
_, err = s.PopSigElements()
|
_, err = s.PopSigElements()
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
b1 := []byte("smth")
|
b1 := []byte("smth")
|
||||||
b2 := []byte("strange")
|
b2 := []byte("strange")
|
||||||
s.PushVal([]StackItem{NewByteArrayItem(b1), NewByteArrayItem(b2)})
|
s.PushVal([]stackitem.Item{stackitem.NewByteArray(b1), stackitem.NewByteArray(b2)})
|
||||||
z, err := s.PopSigElements()
|
z, err := s.PopSigElements()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, z, [][]byte{b1, b2})
|
assert.Equal(t, z, [][]byte{b1, b2})
|
||||||
|
|
1010
pkg/vm/stackitem/item.go
Normal file
1010
pkg/vm/stackitem/item.go
Normal file
File diff suppressed because it is too large
Load diff
414
pkg/vm/stackitem/item_test.go
Normal file
414
pkg/vm/stackitem/item_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
134
pkg/vm/stackitem/serialization.go
Normal file
134
pkg/vm/stackitem/serialization.go
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package stackitem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SerializeItem encodes given Item into the byte slice.
|
||||||
|
func SerializeItem(item Item) ([]byte, error) {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
EncodeBinaryStackItem(item, w.BinWriter)
|
||||||
|
if w.Err != nil {
|
||||||
|
return nil, w.Err
|
||||||
|
}
|
||||||
|
return w.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinaryStackItem encodes given Item into the given BinWriter. It's
|
||||||
|
// similar to io.Serializable's EncodeBinary, but works with Item
|
||||||
|
// interface.
|
||||||
|
func EncodeBinaryStackItem(item Item, w *io.BinWriter) {
|
||||||
|
serializeItemTo(item, w, make(map[Item]bool))
|
||||||
|
}
|
||||||
|
|
||||||
|
func serializeItemTo(item Item, w *io.BinWriter, seen map[Item]bool) {
|
||||||
|
if seen[item] {
|
||||||
|
w.Err = errors.New("recursive structures can't be serialized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := item.(type) {
|
||||||
|
case *ByteArray:
|
||||||
|
w.WriteBytes([]byte{byte(ByteArrayT)})
|
||||||
|
w.WriteVarBytes(t.Value().([]byte))
|
||||||
|
case *Buffer:
|
||||||
|
w.WriteBytes([]byte{byte(BufferT)})
|
||||||
|
w.WriteVarBytes(t.Value().([]byte))
|
||||||
|
case *Bool:
|
||||||
|
w.WriteBytes([]byte{byte(BooleanT)})
|
||||||
|
w.WriteBool(t.Value().(bool))
|
||||||
|
case *BigInteger:
|
||||||
|
w.WriteBytes([]byte{byte(IntegerT)})
|
||||||
|
w.WriteVarBytes(bigint.ToBytes(t.Value().(*big.Int)))
|
||||||
|
case *Interop:
|
||||||
|
w.Err = errors.New("interop item can't be serialized")
|
||||||
|
case *Array, *Struct:
|
||||||
|
seen[item] = true
|
||||||
|
|
||||||
|
_, isArray := t.(*Array)
|
||||||
|
if isArray {
|
||||||
|
w.WriteBytes([]byte{byte(ArrayT)})
|
||||||
|
} else {
|
||||||
|
w.WriteBytes([]byte{byte(StructT)})
|
||||||
|
}
|
||||||
|
|
||||||
|
arr := t.Value().([]Item)
|
||||||
|
w.WriteVarUint(uint64(len(arr)))
|
||||||
|
for i := range arr {
|
||||||
|
serializeItemTo(arr[i], w, seen)
|
||||||
|
}
|
||||||
|
case *Map:
|
||||||
|
seen[item] = true
|
||||||
|
|
||||||
|
w.WriteBytes([]byte{byte(MapT)})
|
||||||
|
w.WriteVarUint(uint64(len(t.Value().([]MapElement))))
|
||||||
|
for i := range t.Value().([]MapElement) {
|
||||||
|
serializeItemTo(t.Value().([]MapElement)[i].Key, w, seen)
|
||||||
|
serializeItemTo(t.Value().([]MapElement)[i].Value, w, seen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeserializeItem decodes Item from the given byte slice.
|
||||||
|
func DeserializeItem(data []byte) (Item, error) {
|
||||||
|
r := io.NewBinReaderFromBuf(data)
|
||||||
|
item := DecodeBinaryStackItem(r)
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinaryStackItem decodes previously serialized Item from the given
|
||||||
|
// reader. It's similar to the io.Serializable's DecodeBinary(), but implemented
|
||||||
|
// as a function because Item itself is an interface. Caveat: always check
|
||||||
|
// reader's error value before using the returned Item.
|
||||||
|
func DecodeBinaryStackItem(r *io.BinReader) Item {
|
||||||
|
var t = Type(r.ReadB())
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case ByteArrayT:
|
||||||
|
data := r.ReadVarBytes()
|
||||||
|
return NewByteArray(data)
|
||||||
|
case BooleanT:
|
||||||
|
var b = r.ReadBool()
|
||||||
|
return NewBool(b)
|
||||||
|
case IntegerT:
|
||||||
|
data := r.ReadVarBytes()
|
||||||
|
num := bigint.FromBytes(data)
|
||||||
|
return NewBigInteger(num)
|
||||||
|
case ArrayT, StructT:
|
||||||
|
size := int(r.ReadVarUint())
|
||||||
|
arr := make([]Item, size)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
arr[i] = DecodeBinaryStackItem(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t == ArrayT {
|
||||||
|
return NewArray(arr)
|
||||||
|
}
|
||||||
|
return NewStruct(arr)
|
||||||
|
case MapT:
|
||||||
|
size := int(r.ReadVarUint())
|
||||||
|
m := NewMap()
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
key := DecodeBinaryStackItem(r)
|
||||||
|
value := DecodeBinaryStackItem(r)
|
||||||
|
if r.Err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m.Add(key, value)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
default:
|
||||||
|
r.Err = errors.New("unknown type")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
56
pkg/vm/stackitem/type.go
Normal file
56
pkg/vm/stackitem/type.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
212
pkg/vm/vm.go
212
pkg/vm/vm.go
|
@ -12,9 +12,10 @@ import (
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,14 +53,11 @@ const (
|
||||||
// MaxInvocationStackSize is the maximum size of an invocation stack.
|
// MaxInvocationStackSize is the maximum size of an invocation stack.
|
||||||
MaxInvocationStackSize = 1024
|
MaxInvocationStackSize = 1024
|
||||||
|
|
||||||
// MaxBigIntegerSizeBits is the maximum size of BigInt item in bits.
|
|
||||||
MaxBigIntegerSizeBits = 32 * 8
|
|
||||||
|
|
||||||
// MaxStackSize is the maximum number of items allowed to be
|
// MaxStackSize is the maximum number of items allowed to be
|
||||||
// on all stacks at once.
|
// on all stacks at once.
|
||||||
MaxStackSize = 2 * 1024
|
MaxStackSize = 2 * 1024
|
||||||
|
|
||||||
maxSHLArg = MaxBigIntegerSizeBits
|
maxSHLArg = stackitem.MaxBigIntegerSizeBits
|
||||||
)
|
)
|
||||||
|
|
||||||
// VM represents the virtual machine.
|
// VM represents the virtual machine.
|
||||||
|
@ -165,7 +163,7 @@ func (v *VM) GetPublicKeys() map[string]*keys.PublicKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadArgs loads in the arguments used in the Mian entry point.
|
// LoadArgs loads in the arguments used in the Mian entry point.
|
||||||
func (v *VM) LoadArgs(method []byte, args []StackItem) {
|
func (v *VM) LoadArgs(method []byte, args []stackitem.Item) {
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
v.estack.PushVal(args)
|
v.estack.PushVal(args)
|
||||||
}
|
}
|
||||||
|
@ -513,7 +511,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
if op <= opcode.PUSHINT256 {
|
if op <= opcode.PUSHINT256 {
|
||||||
v.estack.PushVal(emit.BytesToInt(parameter))
|
v.estack.PushVal(bigint.FromBytes(parameter))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,22 +532,22 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if n < 0 || int(n) > len(ctx.prog) {
|
if n < 0 || int(n) > len(ctx.prog) {
|
||||||
panic(fmt.Sprintf("invalid pointer offset (%d)", n))
|
panic(fmt.Sprintf("invalid pointer offset (%d)", n))
|
||||||
}
|
}
|
||||||
ptr := NewPointerItem(int(n), ctx.prog)
|
ptr := stackitem.NewPointer(int(n), ctx.prog)
|
||||||
v.estack.PushVal(ptr)
|
v.estack.PushVal(ptr)
|
||||||
|
|
||||||
case opcode.PUSHNULL:
|
case opcode.PUSHNULL:
|
||||||
v.estack.PushVal(NullItem{})
|
v.estack.PushVal(stackitem.Null{})
|
||||||
|
|
||||||
case opcode.ISNULL:
|
case opcode.ISNULL:
|
||||||
res := v.estack.Pop().value.Equals(NullItem{})
|
res := v.estack.Pop().value.Equals(stackitem.Null{})
|
||||||
v.estack.PushVal(res)
|
v.estack.PushVal(res)
|
||||||
|
|
||||||
case opcode.ISTYPE:
|
case opcode.ISTYPE:
|
||||||
res := v.estack.Pop().Item()
|
res := v.estack.Pop().Item()
|
||||||
v.estack.PushVal(res.Type() == StackItemType(parameter[0]))
|
v.estack.PushVal(res.Type() == stackitem.Type(parameter[0]))
|
||||||
|
|
||||||
case opcode.CONVERT:
|
case opcode.CONVERT:
|
||||||
typ := StackItemType(parameter[0])
|
typ := stackitem.Type(parameter[0])
|
||||||
item := v.estack.Pop().Item()
|
item := v.estack.Pop().Item()
|
||||||
result, err := item.Convert(typ)
|
result, err := item.Convert(typ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -637,7 +635,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if n < 0 || n > MaxItemSize {
|
if n < 0 || n > MaxItemSize {
|
||||||
panic("invalid size")
|
panic("invalid size")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(NewBufferItem(make([]byte, n)))
|
v.estack.PushVal(stackitem.NewBuffer(make([]byte, n)))
|
||||||
|
|
||||||
case opcode.MEMCPY:
|
case opcode.MEMCPY:
|
||||||
n := toInt(v.estack.Pop().BigInt())
|
n := toInt(v.estack.Pop().BigInt())
|
||||||
|
@ -656,7 +654,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if di < 0 {
|
if di < 0 {
|
||||||
panic("invalid destination index")
|
panic("invalid destination index")
|
||||||
}
|
}
|
||||||
dst := v.estack.Pop().value.(*BufferItem).value
|
dst := v.estack.Pop().value.(*stackitem.Buffer).Value().([]byte)
|
||||||
if sum := si + n; sum < 0 || sum > len(dst) {
|
if sum := si + n; sum < 0 || sum > len(dst) {
|
||||||
panic("size is too big")
|
panic("size is too big")
|
||||||
}
|
}
|
||||||
|
@ -669,7 +667,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic(fmt.Sprintf("too big item: %d", l))
|
panic(fmt.Sprintf("too big item: %d", l))
|
||||||
}
|
}
|
||||||
ab := append(a, b...)
|
ab := append(a, b...)
|
||||||
v.estack.PushVal(NewBufferItem(ab))
|
v.estack.PushVal(stackitem.NewBuffer(ab))
|
||||||
|
|
||||||
case opcode.SUBSTR:
|
case opcode.SUBSTR:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -685,7 +683,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if last > len(s) {
|
if last > len(s) {
|
||||||
panic("invalid offset")
|
panic("invalid offset")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(NewBufferItem(s[o:last]))
|
v.estack.PushVal(stackitem.NewBuffer(s[o:last]))
|
||||||
|
|
||||||
case opcode.LEFT:
|
case opcode.LEFT:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -696,7 +694,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if t := len(s); l > t {
|
if t := len(s); l > t {
|
||||||
panic("size is too big")
|
panic("size is too big")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(NewBufferItem(s[:l]))
|
v.estack.PushVal(stackitem.NewBuffer(s[:l]))
|
||||||
|
|
||||||
case opcode.RIGHT:
|
case opcode.RIGHT:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -704,7 +702,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic("negative length")
|
panic("negative length")
|
||||||
}
|
}
|
||||||
s := v.estack.Pop().Bytes()
|
s := v.estack.Pop().Bytes()
|
||||||
v.estack.PushVal(NewBufferItem(s[len(s)-l:]))
|
v.estack.PushVal(stackitem.NewBuffer(s[len(s)-l:]))
|
||||||
|
|
||||||
case opcode.DEPTH:
|
case opcode.DEPTH:
|
||||||
v.estack.PushVal(v.estack.Len())
|
v.estack.PushVal(v.estack.Len())
|
||||||
|
@ -801,7 +799,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
// inplace
|
// inplace
|
||||||
e := v.estack.Peek(0)
|
e := v.estack.Peek(0)
|
||||||
i := e.BigInt()
|
i := e.BigInt()
|
||||||
e.value = makeStackItem(i.Not(i))
|
e.value = stackitem.Make(i.Not(i))
|
||||||
|
|
||||||
case opcode.AND:
|
case opcode.AND:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
|
@ -977,7 +975,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
|
|
||||||
// Object operations
|
// Object operations
|
||||||
case opcode.NEWARRAY0:
|
case opcode.NEWARRAY0:
|
||||||
v.estack.PushVal(&ArrayItem{[]StackItem{}})
|
v.estack.PushVal(stackitem.NewArray([]stackitem.Item{}))
|
||||||
|
|
||||||
case opcode.NEWARRAY, opcode.NEWARRAYT:
|
case opcode.NEWARRAY, opcode.NEWARRAYT:
|
||||||
item := v.estack.Pop()
|
item := v.estack.Pop()
|
||||||
|
@ -985,15 +983,15 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if n > MaxArraySize {
|
if n > MaxArraySize {
|
||||||
panic("too long array")
|
panic("too long array")
|
||||||
}
|
}
|
||||||
typ := AnyT
|
typ := stackitem.AnyT
|
||||||
if op == opcode.NEWARRAYT {
|
if op == opcode.NEWARRAYT {
|
||||||
typ = StackItemType(parameter[0])
|
typ = stackitem.Type(parameter[0])
|
||||||
}
|
}
|
||||||
items := makeArrayOfType(int(n), typ)
|
items := makeArrayOfType(int(n), typ)
|
||||||
v.estack.PushVal(&ArrayItem{items})
|
v.estack.PushVal(stackitem.NewArray(items))
|
||||||
|
|
||||||
case opcode.NEWSTRUCT0:
|
case opcode.NEWSTRUCT0:
|
||||||
v.estack.PushVal(&StructItem{[]StackItem{}})
|
v.estack.PushVal(stackitem.NewStruct([]stackitem.Item{}))
|
||||||
|
|
||||||
case opcode.NEWSTRUCT:
|
case opcode.NEWSTRUCT:
|
||||||
item := v.estack.Pop()
|
item := v.estack.Pop()
|
||||||
|
@ -1001,8 +999,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if n > MaxArraySize {
|
if n > MaxArraySize {
|
||||||
panic("too long struct")
|
panic("too long struct")
|
||||||
}
|
}
|
||||||
items := makeArrayOfType(int(n), AnyT)
|
items := makeArrayOfType(int(n), stackitem.AnyT)
|
||||||
v.estack.PushVal(&StructItem{items})
|
v.estack.PushVal(stackitem.NewStruct(items))
|
||||||
|
|
||||||
case opcode.APPEND:
|
case opcode.APPEND:
|
||||||
itemElem := v.estack.Pop()
|
itemElem := v.estack.Pop()
|
||||||
|
@ -1011,20 +1009,16 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
val := cloneIfStruct(itemElem.value)
|
val := cloneIfStruct(itemElem.value)
|
||||||
|
|
||||||
switch t := arrElem.value.(type) {
|
switch t := arrElem.value.(type) {
|
||||||
case *ArrayItem:
|
case *stackitem.Array:
|
||||||
arr := t.Value().([]StackItem)
|
if t.Len() >= MaxArraySize {
|
||||||
if len(arr) >= MaxArraySize {
|
|
||||||
panic("too long array")
|
panic("too long array")
|
||||||
}
|
}
|
||||||
arr = append(arr, val)
|
t.Append(val)
|
||||||
t.value = arr
|
case *stackitem.Struct:
|
||||||
case *StructItem:
|
if t.Len() >= MaxArraySize {
|
||||||
arr := t.Value().([]StackItem)
|
|
||||||
if len(arr) >= MaxArraySize {
|
|
||||||
panic("too long struct")
|
panic("too long struct")
|
||||||
}
|
}
|
||||||
arr = append(arr, val)
|
t.Append(val)
|
||||||
t.value = arr
|
|
||||||
default:
|
default:
|
||||||
panic("APPEND: not of underlying type Array")
|
panic("APPEND: not of underlying type Array")
|
||||||
}
|
}
|
||||||
|
@ -1037,7 +1031,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic("OPACK: invalid length")
|
panic("OPACK: invalid length")
|
||||||
}
|
}
|
||||||
|
|
||||||
items := make([]StackItem, n)
|
items := make([]stackitem.Item, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
items[i] = v.estack.Pop().value
|
items[i] = v.estack.Pop().value
|
||||||
}
|
}
|
||||||
|
@ -1060,20 +1054,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
index := int(key.BigInt().Int64())
|
index := int(key.BigInt().Int64())
|
||||||
|
|
||||||
switch t := obj.value.(type) {
|
switch t := obj.value.(type) {
|
||||||
// Struct and Array items have their underlying value as []StackItem.
|
// Struct and Array items have their underlying value as []Item.
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
arr := t.Value().([]StackItem)
|
arr := t.Value().([]stackitem.Item)
|
||||||
if index < 0 || index >= len(arr) {
|
if index < 0 || index >= len(arr) {
|
||||||
panic("PICKITEM: invalid index")
|
panic("PICKITEM: invalid index")
|
||||||
}
|
}
|
||||||
item := arr[index].Dup()
|
item := arr[index].Dup()
|
||||||
v.estack.PushVal(item)
|
v.estack.PushVal(item)
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
index := t.Index(key.Item())
|
index := t.Index(key.Item())
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
panic("invalid key")
|
panic("invalid key")
|
||||||
}
|
}
|
||||||
v.estack.Push(&Element{value: t.value[index].Value.Dup()})
|
v.estack.Push(&Element{value: t.Value().([]stackitem.MapElement)[index].Value.Dup()})
|
||||||
default:
|
default:
|
||||||
arr := obj.Bytes()
|
arr := obj.Bytes()
|
||||||
if index < 0 || index >= len(arr) {
|
if index < 0 || index >= len(arr) {
|
||||||
|
@ -1091,9 +1085,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
obj := v.estack.Pop()
|
obj := v.estack.Pop()
|
||||||
|
|
||||||
switch t := obj.value.(type) {
|
switch t := obj.value.(type) {
|
||||||
// Struct and Array items have their underlying value as []StackItem.
|
// Struct and Array items have their underlying value as []Item.
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
arr := t.Value().([]StackItem)
|
arr := t.Value().([]stackitem.Item)
|
||||||
index := int(key.BigInt().Int64())
|
index := int(key.BigInt().Int64())
|
||||||
if index < 0 || index >= len(arr) {
|
if index < 0 || index >= len(arr) {
|
||||||
panic("SETITEM: invalid index")
|
panic("SETITEM: invalid index")
|
||||||
|
@ -1101,18 +1095,18 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
v.refs.Remove(arr[index])
|
v.refs.Remove(arr[index])
|
||||||
arr[index] = item
|
arr[index] = item
|
||||||
v.refs.Add(arr[index])
|
v.refs.Add(arr[index])
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
if i := t.Index(key.value); i >= 0 {
|
if i := t.Index(key.value); i >= 0 {
|
||||||
v.refs.Remove(t.value[i].Value)
|
v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value)
|
||||||
} else if len(t.value) >= MaxArraySize {
|
} else if t.Len() >= MaxArraySize {
|
||||||
panic("too big map")
|
panic("too big map")
|
||||||
}
|
}
|
||||||
t.Add(key.value, item)
|
t.Add(key.value, item)
|
||||||
v.refs.Add(item)
|
v.refs.Add(item)
|
||||||
|
|
||||||
case *BufferItem:
|
case *stackitem.Buffer:
|
||||||
index := toInt(key.BigInt())
|
index := toInt(key.BigInt())
|
||||||
if index < 0 || index >= len(t.value) {
|
if index < 0 || index >= t.Len() {
|
||||||
panic("invalid index")
|
panic("invalid index")
|
||||||
}
|
}
|
||||||
bi, err := item.TryInteger()
|
bi, err := item.TryInteger()
|
||||||
|
@ -1120,7 +1114,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if err != nil || b < math.MinInt8 || b > math.MaxUint8 {
|
if err != nil || b < math.MinInt8 || b > math.MaxUint8 {
|
||||||
panic("invalid value")
|
panic("invalid value")
|
||||||
}
|
}
|
||||||
t.value[index] = byte(b)
|
t.Value().([]byte)[index] = byte(b)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
|
panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
|
||||||
|
@ -1129,14 +1123,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
case opcode.REVERSEITEMS:
|
case opcode.REVERSEITEMS:
|
||||||
item := v.estack.Pop()
|
item := v.estack.Pop()
|
||||||
switch t := item.value.(type) {
|
switch t := item.value.(type) {
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
a := t.Value().([]StackItem)
|
a := t.Value().([]stackitem.Item)
|
||||||
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
|
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
|
||||||
a[i], a[j] = a[j], a[i]
|
a[i], a[j] = a[j], a[i]
|
||||||
}
|
}
|
||||||
case *BufferItem:
|
case *stackitem.Buffer:
|
||||||
for i, j := 0, len(t.value)-1; i < j; i, j = i+1, j-1 {
|
for i, j := 0, t.Len()-1; i < j; i, j = i+1, j-1 {
|
||||||
t.value[i], t.value[j] = t.value[j], t.value[i]
|
t.Value().([]byte)[i], t.Value().([]byte)[j] = t.Value().([]byte)[j], t.Value().([]byte)[i]
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid item type %s", t))
|
panic(fmt.Sprintf("invalid item type %s", t))
|
||||||
|
@ -1147,29 +1141,27 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
|
|
||||||
elem := v.estack.Pop()
|
elem := v.estack.Pop()
|
||||||
switch t := elem.value.(type) {
|
switch t := elem.value.(type) {
|
||||||
case *ArrayItem:
|
case *stackitem.Array:
|
||||||
a := t.value
|
a := t.Value().([]stackitem.Item)
|
||||||
k := int(key.BigInt().Int64())
|
k := int(key.BigInt().Int64())
|
||||||
if k < 0 || k >= len(a) {
|
if k < 0 || k >= len(a) {
|
||||||
panic("REMOVE: invalid index")
|
panic("REMOVE: invalid index")
|
||||||
}
|
}
|
||||||
v.refs.Remove(a[k])
|
v.refs.Remove(a[k])
|
||||||
a = append(a[:k], a[k+1:]...)
|
t.Remove(k)
|
||||||
t.value = a
|
case *stackitem.Struct:
|
||||||
case *StructItem:
|
a := t.Value().([]stackitem.Item)
|
||||||
a := t.value
|
|
||||||
k := int(key.BigInt().Int64())
|
k := int(key.BigInt().Int64())
|
||||||
if k < 0 || k >= len(a) {
|
if k < 0 || k >= len(a) {
|
||||||
panic("REMOVE: invalid index")
|
panic("REMOVE: invalid index")
|
||||||
}
|
}
|
||||||
v.refs.Remove(a[k])
|
v.refs.Remove(a[k])
|
||||||
a = append(a[:k], a[k+1:]...)
|
t.Remove(k)
|
||||||
t.value = a
|
case *stackitem.Map:
|
||||||
case *MapItem:
|
|
||||||
index := t.Index(key.Item())
|
index := t.Index(key.Item())
|
||||||
// NEO 2.0 doesn't error on missing key.
|
// NEO 2.0 doesn't error on missing key.
|
||||||
if index >= 0 {
|
if index >= 0 {
|
||||||
v.refs.Remove(t.value[index].Value)
|
v.refs.Remove(t.Value().([]stackitem.MapElement)[index].Value)
|
||||||
t.Drop(index)
|
t.Drop(index)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -1179,21 +1171,21 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
case opcode.CLEARITEMS:
|
case opcode.CLEARITEMS:
|
||||||
elem := v.estack.Pop()
|
elem := v.estack.Pop()
|
||||||
switch t := elem.value.(type) {
|
switch t := elem.value.(type) {
|
||||||
case *ArrayItem:
|
case *stackitem.Array:
|
||||||
for _, item := range t.value {
|
for _, item := range t.Value().([]stackitem.Item) {
|
||||||
v.refs.Remove(item)
|
v.refs.Remove(item)
|
||||||
}
|
}
|
||||||
t.value = t.value[:0]
|
t.Clear()
|
||||||
case *StructItem:
|
case *stackitem.Struct:
|
||||||
for _, item := range t.value {
|
for _, item := range t.Value().([]stackitem.Item) {
|
||||||
v.refs.Remove(item)
|
v.refs.Remove(item)
|
||||||
}
|
}
|
||||||
t.value = t.value[:0]
|
t.Clear()
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
for i := range t.value {
|
for i := range t.Value().([]stackitem.MapElement) {
|
||||||
v.refs.Remove(t.value[i].Value)
|
v.refs.Remove(t.Value().([]stackitem.MapElement)[i].Value)
|
||||||
}
|
}
|
||||||
t.value = t.value[:0]
|
t.Clear()
|
||||||
default:
|
default:
|
||||||
panic("CLEARITEMS: invalid type")
|
panic("CLEARITEMS: invalid type")
|
||||||
}
|
}
|
||||||
|
@ -1203,9 +1195,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
// Cause there is no native (byte) item type here, hence we need to check
|
// Cause there is no native (byte) item type here, hence we need to check
|
||||||
// the type of the item for array size operations.
|
// the type of the item for array size operations.
|
||||||
switch t := elem.Value().(type) {
|
switch t := elem.Value().(type) {
|
||||||
case []StackItem:
|
case []stackitem.Item:
|
||||||
v.estack.PushVal(len(t))
|
v.estack.PushVal(len(t))
|
||||||
case []MapElement:
|
case []stackitem.MapElement:
|
||||||
v.estack.PushVal(len(t))
|
v.estack.PushVal(len(t))
|
||||||
default:
|
default:
|
||||||
v.estack.PushVal(len(elem.Bytes()))
|
v.estack.PushVal(len(elem.Bytes()))
|
||||||
|
@ -1241,8 +1233,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
v.jumpIf(newCtx, offset, true)
|
v.jumpIf(newCtx, offset, true)
|
||||||
|
|
||||||
case opcode.CALLA:
|
case opcode.CALLA:
|
||||||
ptr := v.estack.Pop().Item().(*PointerItem)
|
ptr := v.estack.Pop().Item().(*stackitem.Pointer)
|
||||||
if ptr.hash != ctx.ScriptHash() {
|
if ptr.ScriptHash() != ctx.ScriptHash() {
|
||||||
panic("invalid script in pointer")
|
panic("invalid script in pointer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1251,7 +1243,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
newCtx.arguments = nil
|
newCtx.arguments = nil
|
||||||
newCtx.rvcount = -1
|
newCtx.rvcount = -1
|
||||||
v.istack.PushVal(newCtx)
|
v.istack.PushVal(newCtx)
|
||||||
v.jumpIf(newCtx, ptr.pos, true)
|
v.jumpIf(newCtx, ptr.Position(), true)
|
||||||
|
|
||||||
case opcode.SYSCALL:
|
case opcode.SYSCALL:
|
||||||
interopID := GetInteropID(parameter)
|
interopID := GetInteropID(parameter)
|
||||||
|
@ -1291,7 +1283,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
case opcode.NEWMAP:
|
case opcode.NEWMAP:
|
||||||
v.estack.Push(&Element{value: NewMapItem()})
|
v.estack.Push(&Element{value: stackitem.NewMap()})
|
||||||
|
|
||||||
case opcode.KEYS:
|
case opcode.KEYS:
|
||||||
item := v.estack.Pop()
|
item := v.estack.Pop()
|
||||||
|
@ -1299,14 +1291,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic("no argument")
|
panic("no argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
m, ok := item.value.(*MapItem)
|
m, ok := item.value.(*stackitem.Map)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("not a Map")
|
panic("not a Map")
|
||||||
}
|
}
|
||||||
|
|
||||||
arr := make([]StackItem, 0, len(m.value))
|
arr := make([]stackitem.Item, 0, m.Len())
|
||||||
for k := range m.value {
|
for k := range m.Value().([]stackitem.MapElement) {
|
||||||
arr = append(arr, m.value[k].Key.Dup())
|
arr = append(arr, m.Value().([]stackitem.MapElement)[k].Key.Dup())
|
||||||
}
|
}
|
||||||
v.estack.PushVal(arr)
|
v.estack.PushVal(arr)
|
||||||
|
|
||||||
|
@ -1316,18 +1308,18 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic("no argument")
|
panic("no argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
var arr []StackItem
|
var arr []stackitem.Item
|
||||||
switch t := item.value.(type) {
|
switch t := item.value.(type) {
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
src := t.Value().([]StackItem)
|
src := t.Value().([]stackitem.Item)
|
||||||
arr = make([]StackItem, len(src))
|
arr = make([]stackitem.Item, len(src))
|
||||||
for i := range src {
|
for i := range src {
|
||||||
arr[i] = cloneIfStruct(src[i])
|
arr[i] = cloneIfStruct(src[i])
|
||||||
}
|
}
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
arr = make([]StackItem, 0, len(t.value))
|
arr = make([]stackitem.Item, 0, t.Len())
|
||||||
for k := range t.value {
|
for k := range t.Value().([]stackitem.MapElement) {
|
||||||
arr = append(arr, cloneIfStruct(t.value[k].Value))
|
arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("not a Map, Array or Struct")
|
panic("not a Map, Array or Struct")
|
||||||
|
@ -1344,20 +1336,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic("no value found")
|
panic("no value found")
|
||||||
}
|
}
|
||||||
switch t := c.value.(type) {
|
switch t := c.value.(type) {
|
||||||
case *ArrayItem, *StructItem:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
index := key.BigInt().Int64()
|
index := key.BigInt().Int64()
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
panic("negative index")
|
panic("negative index")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(index < int64(len(c.Array())))
|
v.estack.PushVal(index < int64(len(c.Array())))
|
||||||
case *MapItem:
|
case *stackitem.Map:
|
||||||
v.estack.PushVal(t.Has(key.Item()))
|
v.estack.PushVal(t.Has(key.Item()))
|
||||||
case *BufferItem:
|
case *stackitem.Buffer:
|
||||||
index := key.BigInt().Int64()
|
index := key.BigInt().Int64()
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
panic("negative index")
|
panic("negative index")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(index < int64(len(t.value)))
|
v.estack.PushVal(index < int64(t.Len()))
|
||||||
default:
|
default:
|
||||||
panic("wrong collection type")
|
panic("wrong collection type")
|
||||||
}
|
}
|
||||||
|
@ -1536,30 +1528,30 @@ func checkMultisig1(v *VM, h []byte, pkeys [][]byte, sig []byte) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneIfStruct(item StackItem) StackItem {
|
func cloneIfStruct(item stackitem.Item) stackitem.Item {
|
||||||
switch it := item.(type) {
|
switch it := item.(type) {
|
||||||
case *StructItem:
|
case *stackitem.Struct:
|
||||||
return it.Clone()
|
return it.Clone()
|
||||||
default:
|
default:
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeArrayOfType(n int, typ StackItemType) []StackItem {
|
func makeArrayOfType(n int, typ stackitem.Type) []stackitem.Item {
|
||||||
if !typ.IsValid() {
|
if !typ.IsValid() {
|
||||||
panic(fmt.Sprintf("invalid stack item type: %d", typ))
|
panic(fmt.Sprintf("invalid stack item type: %d", typ))
|
||||||
}
|
}
|
||||||
items := make([]StackItem, n)
|
items := make([]stackitem.Item, n)
|
||||||
for i := range items {
|
for i := range items {
|
||||||
switch typ {
|
switch typ {
|
||||||
case BooleanT:
|
case stackitem.BooleanT:
|
||||||
items[i] = NewBoolItem(false)
|
items[i] = stackitem.NewBool(false)
|
||||||
case IntegerT:
|
case stackitem.IntegerT:
|
||||||
items[i] = NewBigIntegerItem(big.NewInt(0))
|
items[i] = stackitem.NewBigInteger(big.NewInt(0))
|
||||||
case ByteArrayT:
|
case stackitem.ByteArrayT:
|
||||||
items[i] = NewByteArrayItem([]byte{})
|
items[i] = stackitem.NewByteArray([]byte{})
|
||||||
default:
|
default:
|
||||||
items[i] = NullItem{}
|
items[i] = stackitem.Null{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
|
@ -1569,7 +1561,7 @@ func validateMapKey(key *Element) {
|
||||||
if key == nil {
|
if key == nil {
|
||||||
panic("no key found")
|
panic("no key found")
|
||||||
}
|
}
|
||||||
if !isValidMapKey(key.Item()) {
|
if !stackitem.IsValidMapKey(key.Item()) {
|
||||||
panic("key can't be a collection")
|
panic("key can't be a collection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue