From 7c3d7c026108750801d6a20642c1362e5719f3bb Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 24 Apr 2020 13:48:19 +0300 Subject: [PATCH] vm: implement NEWARRAYT opcode --- pkg/vm/context.go | 2 +- pkg/vm/opcode/opcode.go | 10 +++++----- pkg/vm/opcode/opcode_string.go | 36 +++++++++++++++++++--------------- pkg/vm/vm.go | 26 +++++++++++++++++++----- pkg/vm/vm_test.go | 30 ++++++++++++++++++++++++++-- 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 362922bf0..8296f2a8c 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -101,7 +101,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) { } case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.JMPEQ, opcode.JMPNE, opcode.JMPGT, opcode.JMPGE, opcode.JMPLT, opcode.JMPLE, - opcode.CALL, opcode.ISTYPE: + opcode.CALL, opcode.ISTYPE, opcode.NEWARRAYT: numtoread = 1 case opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL, opcode.JMPEQL, opcode.JMPNEL, opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL, diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index 52e258d88..e6f3aacab 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -143,11 +143,11 @@ const ( CHECKMULTISIG Opcode = 0xAE // Advanced data structures (arrays, structures, maps) - PACK Opcode = 0xC0 - UNPACK Opcode = 0xC1 - NEWARRAY0 Opcode = 0xC2 - NEWARRAY Opcode = 0xC3 - // NEWARRAYT Opcode = 0xC4 + PACK Opcode = 0xC0 + UNPACK Opcode = 0xC1 + NEWARRAY0 Opcode = 0xC2 + NEWARRAY Opcode = 0xC3 + NEWARRAYT Opcode = 0xC4 NEWSTRUCT0 Opcode = 0xC5 NEWSTRUCT Opcode = 0xC6 NEWMAP Opcode = 0xC8 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index 3cfb3be04..b550db451 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -126,6 +126,7 @@ func _() { _ = x[UNPACK-193] _ = x[NEWARRAY0-194] _ = x[NEWARRAY-195] + _ = x[NEWARRAYT-196] _ = x[NEWSTRUCT0-197] _ = x[NEWSTRUCT-198] _ = x[NEWMAP-200] @@ -139,11 +140,12 @@ func _() { _ = x[REVERSEITEMS-209] _ = x[REMOVE-210] _ = x[CLEARITEMS-211] + _ = x[ISTYPE-217] _ = x[THROW-240] _ = x[THROWIFNOT-241] } -const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLOLDPUSH1RETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGPACKUNPACKNEWARRAY0NEWARRAYNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSTHROWTHROWIFNOT" +const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLOLDPUSH1RETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISTYPETHROWTHROWIFNOT" var _Opcode_map = map[Opcode]string{ 0: _Opcode_name[0:8], @@ -262,21 +264,23 @@ var _Opcode_map = map[Opcode]string{ 193: _Opcode_name[621:627], 194: _Opcode_name[627:636], 195: _Opcode_name[636:644], - 197: _Opcode_name[644:654], - 198: _Opcode_name[654:663], - 200: _Opcode_name[663:669], - 202: _Opcode_name[669:673], - 203: _Opcode_name[673:679], - 204: _Opcode_name[679:683], - 205: _Opcode_name[683:689], - 206: _Opcode_name[689:697], - 207: _Opcode_name[697:703], - 208: _Opcode_name[703:710], - 209: _Opcode_name[710:722], - 210: _Opcode_name[722:728], - 211: _Opcode_name[728:738], - 240: _Opcode_name[738:743], - 241: _Opcode_name[743:753], + 196: _Opcode_name[644:653], + 197: _Opcode_name[653:663], + 198: _Opcode_name[663:672], + 200: _Opcode_name[672:678], + 202: _Opcode_name[678:682], + 203: _Opcode_name[682:688], + 204: _Opcode_name[688:692], + 205: _Opcode_name[692:698], + 206: _Opcode_name[698:706], + 207: _Opcode_name[706:712], + 208: _Opcode_name[712:719], + 209: _Opcode_name[719:731], + 210: _Opcode_name[731:737], + 211: _Opcode_name[737:747], + 217: _Opcode_name[747:753], + 240: _Opcode_name[753:758], + 241: _Opcode_name[758:768], } func (i Opcode) String() string { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 4682b37ac..e09663755 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -919,7 +919,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.NEWARRAY0: v.estack.PushVal(&ArrayItem{[]StackItem{}}) - case opcode.NEWARRAY: + case opcode.NEWARRAY, opcode.NEWARRAYT: item := v.estack.Pop() switch t := item.value.(type) { case *StructItem: @@ -933,7 +933,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro if n > MaxArraySize { panic("too long array") } - items := makeArrayOfFalses(int(n)) + typ := BooleanT + if op == opcode.NEWARRAYT { + typ = StackItemType(parameter[0]) + } + items := makeArrayOfType(int(n), typ) v.estack.PushVal(&ArrayItem{items}) } @@ -954,7 +958,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro if n > MaxArraySize { panic("too long struct") } - items := makeArrayOfFalses(int(n)) + items := makeArrayOfType(int(n), BooleanT) v.estack.PushVal(&StructItem{items}) } @@ -1492,10 +1496,22 @@ func cloneIfStruct(item StackItem) StackItem { } } -func makeArrayOfFalses(n int) []StackItem { +func makeArrayOfType(n int, typ StackItemType) []StackItem { + if !typ.IsValid() { + panic(fmt.Sprintf("invalid stack item type: %d", typ)) + } items := make([]StackItem, n) for i := range items { - items[i] = &BoolItem{false} + switch typ { + case BooleanT: + items[i] = NewBoolItem(false) + case IntegerT: + items[i] = NewBigIntegerItem(big.NewInt(0)) + case ByteArrayT: + items[i] = NewByteArrayItem([]byte{}) + default: + items[i] = NullItem{} + } } return items } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index e57e97e80..90d08df36 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -660,7 +660,7 @@ func TestSerializeArray(t *testing.T) { func TestSerializeArrayBad(t *testing.T) { vm := load(getSerializeProg()) - item := NewArrayItem(makeArrayOfFalses(2)) + item := NewArrayItem(makeArrayOfType(2, BooleanT)) item.value[1] = item vm.estack.Push(&Element{value: item}) @@ -1337,7 +1337,7 @@ func testNEWARRAYIssue437(t *testing.T, i1, i2 opcode.Opcode, appended bool) { vm := load(prog) vm.Run() - arr := makeArrayOfFalses(4) + arr := makeArrayOfType(4, BooleanT) arr[2] = makeStackItem(3) arr[3] = makeStackItem(4) if appended { @@ -1379,6 +1379,32 @@ func TestNEWARRAYByteArray(t *testing.T) { assert.Equal(t, &ArrayItem{[]StackItem{}}, vm.estack.Pop().value) } +func testNEWARRAYT(t *testing.T, typ StackItemType, item StackItem) { + prog := makeProgram(opcode.NEWARRAYT, opcode.Opcode(typ), opcode.PUSH0, opcode.PICKITEM) + v := load(prog) + v.estack.PushVal(1) + if item == nil { + checkVMFailed(t, v) + return + } + runVM(t, v) + require.Equal(t, 1, v.estack.Len()) + require.Equal(t, item, v.estack.Pop().Item()) +} + +func TestNEWARRAYT(t *testing.T) { + testCases := map[StackItemType]StackItem{ + BooleanT: NewBoolItem(false), + IntegerT: NewBigIntegerItem(big.NewInt(0)), + ByteArrayT: NewByteArrayItem([]byte{}), + ArrayT: NullItem{}, + 0xFF: nil, + } + for typ, item := range testCases { + t.Run(typ.String(), func(t *testing.T) { testNEWARRAYT(t, typ, item) }) + } +} + func TestNEWARRAYBadSize(t *testing.T) { prog := makeProgram(opcode.NEWARRAY) vm := load(prog)