From 7fcd537b098cb9a0b9fd885e13bf55b596ccb5e6 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 6 May 2020 16:31:52 +0300 Subject: [PATCH] vm: implement Pointer stack item Pointer is a generic address type in VM. It is used for calling lambda-expressions. --- pkg/vm/stack_item.go | 83 +++++++++++++++++++++++++++++++++++++++ pkg/vm/stack_item_test.go | 30 ++++++++++++++ pkg/vm/vm_test.go | 6 ++- 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/pkg/vm/stack_item.go b/pkg/vm/stack_item.go index bba8b8fc3..675105f6f 100644 --- a/pkg/vm/stack_item.go +++ b/pkg/vm/stack_item.go @@ -10,7 +10,9 @@ import ( "math/big" "reflect" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) @@ -857,3 +859,84 @@ func (i *InteropItem) Convert(typ StackItemType) (StackItem, error) { func (i *InteropItem) MarshalJSON() ([]byte, error) { return json.Marshal(i.value) } + +// PointerItem represents VM-level instruction pointer. +type PointerItem struct { + pos int + script []byte + hash util.Uint160 +} + +// NewPointerItem returns new pointer on the specified position. +func NewPointerItem(pos int, script []byte) *PointerItem { + return &PointerItem{ + pos: pos, + script: script, + hash: hash.Hash160(script), + } +} + +// String implements StackItem interface. +func (p *PointerItem) String() string { + return "Pointer" +} + +// Value implements StackItem interface. +func (p *PointerItem) Value() interface{} { + return p.pos +} + +// Dup implements StackItem interface. +func (p *PointerItem) Dup() StackItem { + return &PointerItem{ + pos: p.pos, + script: p.script, + hash: p.hash, + } +} + +// Bool implements StackItem interface. +func (p *PointerItem) Bool() bool { + return true +} + +// TryBytes implements StackItem interface. +func (p *PointerItem) TryBytes() ([]byte, error) { + return nil, errors.New("can't convert Pointer to ByteArray") +} + +// TryInteger implements StackItem interface. +func (p *PointerItem) TryInteger() (*big.Int, error) { + return nil, errors.New("can't convert Pointer to Integer") +} + +// Equals implements StackItem interface. +func (p *PointerItem) Equals(s StackItem) bool { + if p == s { + return true + } + ptr, ok := s.(*PointerItem) + return ok && p.pos == ptr.pos && p.hash == ptr.hash +} + +// ToContractParameter implements StackItem interface. +func (p *PointerItem) ToContractParameter(map[StackItem]bool) smartcontract.Parameter { + return smartcontract.NewParameter(smartcontract.AnyType) +} + +// Type implements StackItem interface. +func (p *PointerItem) Type() StackItemType { + return PointerT +} + +// Convert implements StackItem interface. +func (p *PointerItem) Convert(typ StackItemType) (StackItem, error) { + switch typ { + case PointerT: + return p, nil + case BooleanT: + return NewBoolItem(p.Bool()), nil + default: + return nil, errInvalidConversion + } +} diff --git a/pkg/vm/stack_item_test.go b/pkg/vm/stack_item_test.go index 9587e5e0a..fb2f3633f 100644 --- a/pkg/vm/stack_item_test.go +++ b/pkg/vm/stack_item_test.go @@ -127,6 +127,10 @@ var stringerTestCases = []struct { input: NewInteropItem(nil), result: "InteropItem", }, + { + input: NewPointerItem(0, nil), + result: "Pointer", + }, } func TestStringer(t *testing.T) { @@ -314,6 +318,32 @@ var equalsTestCases = map[string][]struct { 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) { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index ad1386d1c..a031fc2eb 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -266,6 +266,7 @@ func TestCONVERT(t *testing.T) { NewArrayItem(arr), NewArrayItem(nil), NewStructItem(arr), NewStructItem(nil), NewMapItem(), m, NewInteropItem(struct{}{}), + NewPointerItem(0, []byte{}), } for i := range trueCases { t.Run(getName(trueCases[i], BooleanT), testBool(trueCases[i], NewBoolItem(true))) @@ -329,9 +330,12 @@ func TestCONVERT(t *testing.T) { t.Run("Map->Map", testCONVERT(MapT, m, m)) + ptr := NewPointerItem(1, []byte{1}) + t.Run("Pointer->Pointer", testCONVERT(PointerT, ptr, ptr)) + t.Run("Null->", func(t *testing.T) { types := []StackItemType{ - BooleanT, ByteArrayT, IntegerT, ArrayT, StructT, MapT, InteropT, + BooleanT, ByteArrayT, IntegerT, ArrayT, StructT, MapT, InteropT, PointerT, } for i := range types { t.Run(types[i].String(), testCONVERT(types[i], NullItem{}, NullItem{}))