Merge pull request #907 from nspcc-dev/feature/compound

vm: implement Array/Struct/Map opcodes
This commit is contained in:
Roman Khimov 2020-04-27 12:10:40 +03:00 committed by GitHub
commit aa8d4d2f57
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 447 additions and 204 deletions

View file

@ -398,7 +398,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.High) ast.Walk(c, n.High)
} else { } else {
emit.Opcode(c.prog.BinWriter, opcode.OVER) emit.Opcode(c.prog.BinWriter, opcode.OVER)
emit.Opcode(c.prog.BinWriter, opcode.ARRAYSIZE) emit.Opcode(c.prog.BinWriter, opcode.SIZE)
} }
emit.Opcode(c.prog.BinWriter, opcode.OVER) emit.Opcode(c.prog.BinWriter, opcode.OVER)
@ -858,7 +858,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.X) ast.Walk(c, n.X)
emit.Opcode(c.prog.BinWriter, opcode.ARRAYSIZE) emit.Opcode(c.prog.BinWriter, opcode.SIZE)
emit.Opcode(c.prog.BinWriter, opcode.PUSH0) emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
c.pushStackLabel(label, 2) c.pushStackLabel(label, 2)
@ -1030,13 +1030,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
switch name { switch name {
case "len": case "len":
arg := expr.Args[0]
typ := c.typeInfo.Types[arg].Type
if isStringType(typ) {
emit.Opcode(c.prog.BinWriter, opcode.SIZE) emit.Opcode(c.prog.BinWriter, opcode.SIZE)
} else {
emit.Opcode(c.prog.BinWriter, opcode.ARRAYSIZE)
}
case "append": case "append":
arg := expr.Args[0] arg := expr.Args[0]
typ := c.typeInfo.Types[arg].Type typ := c.typeInfo.Types[arg].Type

View file

@ -28,34 +28,34 @@ func TestInvocationScriptCreationGood(t *testing.T) {
script: "0c023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}},
script: "10c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "10c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ByteArrayType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ByteArrayType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "0c1450befd26fdf6e4d957c11e078b24ebce6291456f11c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c1450befd26fdf6e4d957c11e078b24ebce6291456f11c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.SignatureType, Value: Param{Type: StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.SignatureType, Value: Param{Type: StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}},
script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash160Type, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash160Type, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash256Type, Value: Param{Type: StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash256Type, Value: Param{Type: StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}},
script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}},
script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: NumberT, Value: 42}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: NumberT, Value: 42}}}}}},
script: "002a11c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "002a11c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "true"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "true"}}}}}},
script: "1111c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "1111c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "false"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "false"}}}}}},
script: "1011c10c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "1011c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}} }}
for _, ps := range paramScripts { for _, ps := range paramScripts {
script, err := CreateFunctionInvocationScript(contract, ps.ps) script, err := CreateFunctionInvocationScript(contract, ps.ps)

View file

@ -48,18 +48,18 @@ type rpcTestCase struct {
check func(t *testing.T, e *executor, result interface{}) check func(t *testing.T, e *executor, result interface{})
} }
const testContractHash = "6b9e88be61028590ebbb1296cbee09beed4ae75d" const testContractHash = "7858559a8405f4e4e1834fcff274ddf4f1b8f407"
var rpcTestCases = map[string][]rpcTestCase{ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
{ {
name: "positive", name: "positive",
params: `["4459d8050e0abb0a42a9ec5a84dee1c5f8c449b11859265ef45a47c5ea369bc5"]`, params: `["4108062977676178e8453a8ef84a702e01bb35af8a65c7529d04704fcb5f1e0e"]`,
result: func(e *executor) interface{} { return &result.ApplicationLog{} }, result: func(e *executor) interface{} { return &result.ApplicationLog{} },
check: func(t *testing.T, e *executor, acc interface{}) { check: func(t *testing.T, e *executor, acc interface{}) {
res, ok := acc.(*result.ApplicationLog) res, ok := acc.(*result.ApplicationLog)
require.True(t, ok) require.True(t, ok)
expectedTxHash, err := util.Uint256DecodeStringLE("4459d8050e0abb0a42a9ec5a84dee1c5f8c449b11859265ef45a47c5ea369bc5") expectedTxHash, err := util.Uint256DecodeStringLE("4108062977676178e8453a8ef84a702e01bb35af8a65c7529d04704fcb5f1e0e")
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, expectedTxHash, res.TxHash) assert.Equal(t, expectedTxHash, res.TxHash)
assert.Equal(t, 1, len(res.Executions)) assert.Equal(t, 1, len(res.Executions))

Binary file not shown.

Binary file not shown.

View file

@ -101,7 +101,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
} }
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.JMPEQ, opcode.JMPNE, case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.JMPEQ, opcode.JMPNE,
opcode.JMPGT, opcode.JMPGE, opcode.JMPLT, opcode.JMPLE, opcode.JMPGT, opcode.JMPGE, opcode.JMPLT, opcode.JMPLE,
opcode.CALL: opcode.CALL, opcode.ISTYPE, opcode.NEWARRAYT:
numtoread = 1 numtoread = 1
case opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL, opcode.JMPEQL, opcode.JMPNEL, case opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL, opcode.JMPEQL, opcode.JMPNEL,
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL, opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL,
@ -186,6 +186,9 @@ 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.
func (c *Context) Type() StackItemType { panic("Context cannot appear on evaluation stack") }
// Equals implements StackItem interface. // Equals implements StackItem interface.
func (c *Context) Equals(s StackItem) bool { func (c *Context) Equals(s StackItem) bool {
return c == s return c == s

View file

@ -98,7 +98,6 @@ const (
SUBSTR Opcode = 0x7F SUBSTR Opcode = 0x7F
LEFT Opcode = 0x80 LEFT Opcode = 0x80
RIGHT Opcode = 0x81 RIGHT Opcode = 0x81
SIZE Opcode = 0x82
// Bitwise logic // Bitwise logic
INVERT Opcode = 0x83 INVERT Opcode = 0x83
@ -144,20 +143,27 @@ const (
CHECKMULTISIG Opcode = 0xAE CHECKMULTISIG Opcode = 0xAE
// Advanced data structures (arrays, structures, maps) // Advanced data structures (arrays, structures, maps)
ARRAYSIZE Opcode = 0xC0 PACK Opcode = 0xC0
PACK Opcode = 0xC1 UNPACK Opcode = 0xC1
UNPACK Opcode = 0xC2 NEWARRAY0 Opcode = 0xC2
PICKITEM Opcode = 0xC3 NEWARRAY Opcode = 0xC3
SETITEM Opcode = 0xC4 NEWARRAYT Opcode = 0xC4
NEWARRAY Opcode = 0xC5 NEWSTRUCT0 Opcode = 0xC5
NEWSTRUCT Opcode = 0xC6 NEWSTRUCT Opcode = 0xC6
NEWMAP Opcode = 0xC7 NEWMAP Opcode = 0xC8
APPEND Opcode = 0xC8 SIZE Opcode = 0xCA
REVERSE Opcode = 0xC9
REMOVE Opcode = 0xCA
HASKEY Opcode = 0xCB HASKEY Opcode = 0xCB
KEYS Opcode = 0xCC KEYS Opcode = 0xCC
VALUES Opcode = 0xCD VALUES Opcode = 0xCD
PICKITEM Opcode = 0xCE
APPEND Opcode = 0xCF
SETITEM Opcode = 0xD0
REVERSEITEMS Opcode = 0xD1
REMOVE Opcode = 0xD2
CLEARITEMS Opcode = 0xD3
// Types
ISTYPE Opcode = 0xD9
// Exceptions // Exceptions
THROW Opcode = 0xF0 THROW Opcode = 0xF0

View file

@ -85,7 +85,6 @@ func _() {
_ = x[SUBSTR-127] _ = x[SUBSTR-127]
_ = x[LEFT-128] _ = x[LEFT-128]
_ = x[RIGHT-129] _ = x[RIGHT-129]
_ = x[SIZE-130]
_ = x[INVERT-131] _ = x[INVERT-131]
_ = x[AND-132] _ = x[AND-132]
_ = x[OR-133] _ = x[OR-133]
@ -123,25 +122,30 @@ func _() {
_ = x[CHECKSIG-172] _ = x[CHECKSIG-172]
_ = x[VERIFY-173] _ = x[VERIFY-173]
_ = x[CHECKMULTISIG-174] _ = x[CHECKMULTISIG-174]
_ = x[ARRAYSIZE-192] _ = x[PACK-192]
_ = x[PACK-193] _ = x[UNPACK-193]
_ = x[UNPACK-194] _ = x[NEWARRAY0-194]
_ = x[PICKITEM-195] _ = x[NEWARRAY-195]
_ = x[SETITEM-196] _ = x[NEWARRAYT-196]
_ = x[NEWARRAY-197] _ = x[NEWSTRUCT0-197]
_ = x[NEWSTRUCT-198] _ = x[NEWSTRUCT-198]
_ = x[NEWMAP-199] _ = x[NEWMAP-200]
_ = x[APPEND-200] _ = x[SIZE-202]
_ = x[REVERSE-201]
_ = x[REMOVE-202]
_ = x[HASKEY-203] _ = x[HASKEY-203]
_ = x[KEYS-204] _ = x[KEYS-204]
_ = x[VALUES-205] _ = x[VALUES-205]
_ = x[PICKITEM-206]
_ = x[APPEND-207]
_ = x[SETITEM-208]
_ = x[REVERSEITEMS-209]
_ = x[REMOVE-210]
_ = x[CLEARITEMS-211]
_ = x[ISTYPE-217]
_ = x[THROW-240] _ = x[THROW-240]
_ = x[THROWIFNOT-241] _ = x[THROWIFNOT-241]
} }
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LOLDPUSH1RETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTSIZEINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGARRAYSIZEPACKUNPACKPICKITEMSETITEMNEWARRAYNEWSTRUCTNEWMAPAPPENDREVERSEREMOVEHASKEYKEYSVALUESTHROWTHROWIFNOT" const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLOLDPUSH1RETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISTYPETHROWTHROWIFNOT"
var _Opcode_map = map[Opcode]string{ var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8], 0: _Opcode_name[0:8],
@ -174,105 +178,109 @@ var _Opcode_map = map[Opcode]string{
32: _Opcode_name[182:188], 32: _Opcode_name[182:188],
33: _Opcode_name[188:191], 33: _Opcode_name[188:191],
34: _Opcode_name[191:194], 34: _Opcode_name[191:194],
35: _Opcode_name[194:199], 35: _Opcode_name[194:198],
36: _Opcode_name[199:204], 36: _Opcode_name[198:203],
37: _Opcode_name[204:211], 37: _Opcode_name[203:209],
38: _Opcode_name[211:219], 38: _Opcode_name[209:217],
39: _Opcode_name[219:229], 39: _Opcode_name[217:226],
40: _Opcode_name[229:234], 40: _Opcode_name[226:231],
41: _Opcode_name[234:241], 41: _Opcode_name[231:237],
42: _Opcode_name[241:246], 42: _Opcode_name[237:242],
43: _Opcode_name[246:253], 43: _Opcode_name[242:248],
44: _Opcode_name[253:258], 44: _Opcode_name[248:253],
45: _Opcode_name[258:265], 45: _Opcode_name[253:259],
46: _Opcode_name[265:270], 46: _Opcode_name[259:264],
47: _Opcode_name[270:277], 47: _Opcode_name[264:270],
48: _Opcode_name[277:282], 48: _Opcode_name[270:275],
49: _Opcode_name[282:289], 49: _Opcode_name[275:281],
50: _Opcode_name[289:294], 50: _Opcode_name[281:286],
51: _Opcode_name[294:301], 51: _Opcode_name[286:292],
52: _Opcode_name[301:305], 52: _Opcode_name[292:296],
53: _Opcode_name[305:311], 53: _Opcode_name[296:301],
81: _Opcode_name[311:319], 81: _Opcode_name[301:309],
102: _Opcode_name[319:322], 102: _Opcode_name[309:312],
103: _Opcode_name[322:329], 103: _Opcode_name[312:319],
104: _Opcode_name[329:336], 104: _Opcode_name[319:326],
105: _Opcode_name[336:344], 105: _Opcode_name[326:334],
106: _Opcode_name[344:359], 106: _Opcode_name[334:349],
107: _Opcode_name[359:369], 107: _Opcode_name[349:359],
108: _Opcode_name[369:381], 108: _Opcode_name[359:371],
109: _Opcode_name[381:386], 109: _Opcode_name[371:376],
112: _Opcode_name[386:392], 112: _Opcode_name[376:382],
114: _Opcode_name[392:397], 114: _Opcode_name[382:387],
115: _Opcode_name[397:402], 115: _Opcode_name[387:392],
116: _Opcode_name[402:407], 116: _Opcode_name[392:397],
117: _Opcode_name[407:411], 117: _Opcode_name[397:401],
118: _Opcode_name[411:414], 118: _Opcode_name[401:404],
119: _Opcode_name[414:417], 119: _Opcode_name[404:407],
120: _Opcode_name[417:421], 120: _Opcode_name[407:411],
121: _Opcode_name[421:425], 121: _Opcode_name[411:415],
122: _Opcode_name[425:429], 122: _Opcode_name[415:419],
123: _Opcode_name[429:432], 123: _Opcode_name[419:422],
124: _Opcode_name[432:436], 124: _Opcode_name[422:426],
125: _Opcode_name[436:440], 125: _Opcode_name[426:430],
126: _Opcode_name[440:443], 126: _Opcode_name[430:433],
127: _Opcode_name[443:449], 127: _Opcode_name[433:439],
128: _Opcode_name[449:453], 128: _Opcode_name[439:443],
129: _Opcode_name[453:458], 129: _Opcode_name[443:448],
130: _Opcode_name[458:462], 131: _Opcode_name[448:454],
131: _Opcode_name[462:468], 132: _Opcode_name[454:457],
132: _Opcode_name[468:471], 133: _Opcode_name[457:459],
133: _Opcode_name[471:473], 134: _Opcode_name[459:462],
134: _Opcode_name[473:476], 135: _Opcode_name[462:467],
135: _Opcode_name[476:481], 139: _Opcode_name[467:470],
139: _Opcode_name[481:484], 140: _Opcode_name[470:473],
140: _Opcode_name[484:487], 141: _Opcode_name[473:477],
141: _Opcode_name[487:491], 143: _Opcode_name[477:483],
143: _Opcode_name[491:497], 144: _Opcode_name[483:486],
144: _Opcode_name[497:500], 145: _Opcode_name[486:489],
145: _Opcode_name[500:503], 146: _Opcode_name[489:491],
146: _Opcode_name[503:505], 147: _Opcode_name[491:494],
147: _Opcode_name[505:508], 148: _Opcode_name[494:497],
148: _Opcode_name[508:511], 149: _Opcode_name[497:500],
149: _Opcode_name[511:514], 150: _Opcode_name[500:503],
150: _Opcode_name[514:517], 151: _Opcode_name[503:506],
151: _Opcode_name[517:520], 152: _Opcode_name[506:509],
152: _Opcode_name[520:523], 153: _Opcode_name[509:512],
153: _Opcode_name[523:526], 154: _Opcode_name[512:519],
154: _Opcode_name[526:533], 155: _Opcode_name[519:525],
155: _Opcode_name[533:539], 156: _Opcode_name[525:533],
156: _Opcode_name[539:547], 158: _Opcode_name[533:544],
158: _Opcode_name[547:558], 159: _Opcode_name[544:546],
159: _Opcode_name[558:560], 160: _Opcode_name[546:548],
160: _Opcode_name[560:562], 161: _Opcode_name[548:551],
161: _Opcode_name[562:565], 162: _Opcode_name[551:554],
162: _Opcode_name[565:568], 163: _Opcode_name[554:557],
163: _Opcode_name[568:571], 164: _Opcode_name[557:560],
164: _Opcode_name[571:574], 165: _Opcode_name[560:566],
165: _Opcode_name[574:580], 167: _Opcode_name[566:570],
167: _Opcode_name[580:584], 168: _Opcode_name[570:576],
168: _Opcode_name[584:590], 169: _Opcode_name[576:583],
169: _Opcode_name[590:597], 170: _Opcode_name[583:590],
170: _Opcode_name[597:604], 172: _Opcode_name[590:598],
172: _Opcode_name[604:612], 173: _Opcode_name[598:604],
173: _Opcode_name[612:618], 174: _Opcode_name[604:617],
174: _Opcode_name[618:631], 192: _Opcode_name[617:621],
192: _Opcode_name[631:640], 193: _Opcode_name[621:627],
193: _Opcode_name[640:644], 194: _Opcode_name[627:636],
194: _Opcode_name[644:650], 195: _Opcode_name[636:644],
195: _Opcode_name[650:658], 196: _Opcode_name[644:653],
196: _Opcode_name[658:665], 197: _Opcode_name[653:663],
197: _Opcode_name[665:673], 198: _Opcode_name[663:672],
198: _Opcode_name[673:682], 200: _Opcode_name[672:678],
199: _Opcode_name[682:688], 202: _Opcode_name[678:682],
200: _Opcode_name[688:694], 203: _Opcode_name[682:688],
201: _Opcode_name[694:701], 204: _Opcode_name[688:692],
202: _Opcode_name[701:707], 205: _Opcode_name[692:698],
203: _Opcode_name[707:713], 206: _Opcode_name[698:706],
204: _Opcode_name[713:717], 207: _Opcode_name[706:712],
205: _Opcode_name[717:723], 208: _Opcode_name[712:719],
240: _Opcode_name[723:728], 209: _Opcode_name[719:731],
241: _Opcode_name[728:738], 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 { func (i Opcode) String() string {

View file

@ -7,17 +7,61 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
) )
type stackItemType byte // StackItemType represents type of the stack item.
type StackItemType byte
// This block defines all known stack item types.
const ( const (
byteArrayT stackItemType = 0x00 AnyT StackItemType = 0x00
booleanT stackItemType = 0x01 PointerT StackItemType = 0x10
integerT stackItemType = 0x02 BooleanT StackItemType = 0x20
arrayT stackItemType = 0x80 IntegerT StackItemType = 0x21
structT stackItemType = 0x81 ByteArrayT StackItemType = 0x28
mapT stackItemType = 0x82 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. // SerializeItem encodes given StackItem into the byte slice.
func SerializeItem(item StackItem) ([]byte, error) { func SerializeItem(item StackItem) ([]byte, error) {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
@ -43,13 +87,13 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
switch t := item.(type) { switch t := item.(type) {
case *ByteArrayItem: case *ByteArrayItem:
w.WriteBytes([]byte{byte(byteArrayT)}) w.WriteBytes([]byte{byte(ByteArrayT)})
w.WriteVarBytes(t.value) w.WriteVarBytes(t.value)
case *BoolItem: case *BoolItem:
w.WriteBytes([]byte{byte(booleanT)}) w.WriteBytes([]byte{byte(BooleanT)})
w.WriteBool(t.value) w.WriteBool(t.value)
case *BigIntegerItem: case *BigIntegerItem:
w.WriteBytes([]byte{byte(integerT)}) w.WriteBytes([]byte{byte(IntegerT)})
w.WriteVarBytes(emit.IntToBytes(t.value)) w.WriteVarBytes(emit.IntToBytes(t.value))
case *InteropItem: case *InteropItem:
w.Err = errors.New("interop item can't be serialized") w.Err = errors.New("interop item can't be serialized")
@ -58,9 +102,9 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
_, isArray := t.(*ArrayItem) _, isArray := t.(*ArrayItem)
if isArray { if isArray {
w.WriteBytes([]byte{byte(arrayT)}) w.WriteBytes([]byte{byte(ArrayT)})
} else { } else {
w.WriteBytes([]byte{byte(structT)}) w.WriteBytes([]byte{byte(StructT)})
} }
arr := t.Value().([]StackItem) arr := t.Value().([]StackItem)
@ -71,7 +115,7 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
case *MapItem: case *MapItem:
seen[item] = true seen[item] = true
w.WriteBytes([]byte{byte(mapT)}) w.WriteBytes([]byte{byte(MapT)})
w.WriteVarUint(uint64(len(t.value))) w.WriteVarUint(uint64(len(t.value)))
for i := range t.value { for i := range t.value {
serializeItemTo(t.value[i].Key, w, seen) serializeItemTo(t.value[i].Key, w, seen)
@ -100,31 +144,31 @@ func DecodeBinaryStackItem(r *io.BinReader) StackItem {
return nil return nil
} }
switch stackItemType(t) { switch StackItemType(t) {
case byteArrayT: case ByteArrayT:
data := r.ReadVarBytes() data := r.ReadVarBytes()
return NewByteArrayItem(data) return NewByteArrayItem(data)
case booleanT: case BooleanT:
var b = r.ReadBool() var b = r.ReadBool()
return NewBoolItem(b) return NewBoolItem(b)
case integerT: case IntegerT:
data := r.ReadVarBytes() data := r.ReadVarBytes()
num := emit.BytesToInt(data) num := emit.BytesToInt(data)
return &BigIntegerItem{ return &BigIntegerItem{
value: num, value: num,
} }
case arrayT, structT: case ArrayT, StructT:
size := int(r.ReadVarUint()) size := int(r.ReadVarUint())
arr := make([]StackItem, size) arr := make([]StackItem, size)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
arr[i] = DecodeBinaryStackItem(r) arr[i] = DecodeBinaryStackItem(r)
} }
if stackItemType(t) == arrayT { if StackItemType(t) == ArrayT {
return &ArrayItem{value: arr} return &ArrayItem{value: arr}
} }
return &StructItem{value: arr} return &StructItem{value: arr}
case mapT: case MapT:
size := int(r.ReadVarUint()) size := int(r.ReadVarUint())
m := NewMapItem() m := NewMapItem()
for i := 0; i < size; i++ { for i := 0; i < size; i++ {

View file

@ -28,6 +28,8 @@ type StackItem interface {
Equals(s StackItem) bool Equals(s StackItem) bool
// ToContractParameter converts StackItem to smartcontract.Parameter // ToContractParameter converts StackItem to smartcontract.Parameter
ToContractParameter(map[StackItem]bool) smartcontract.Parameter ToContractParameter(map[StackItem]bool) smartcontract.Parameter
// Type returns stack item type.
Type() StackItemType
} }
func makeStackItem(v interface{}) StackItem { func makeStackItem(v interface{}) StackItem {
@ -177,6 +179,9 @@ func (i *StructItem) ToContractParameter(seen map[StackItem]bool) smartcontract.
} }
} }
// Type implements StackItem interface.
func (i *StructItem) Type() StackItemType { return StructT }
// Clone returns a Struct with all Struct fields copied by value. // Clone returns a Struct with all Struct fields copied by value.
// Array fields are still copied by reference. // Array fields are still copied by reference.
func (i *StructItem) Clone() *StructItem { func (i *StructItem) Clone() *StructItem {
@ -235,6 +240,9 @@ func (i NullItem) ToContractParameter(map[StackItem]bool) smartcontract.Paramete
} }
} }
// Type implements StackItem interface.
func (i NullItem) Type() StackItemType { return AnyT }
// BigIntegerItem represents a big integer on the stack. // BigIntegerItem represents a big integer on the stack.
type BigIntegerItem struct { type BigIntegerItem struct {
value *big.Int value *big.Int
@ -300,6 +308,9 @@ func (i *BigIntegerItem) ToContractParameter(map[StackItem]bool) smartcontract.P
} }
} }
// Type implements StackItem interface.
func (i *BigIntegerItem) Type() StackItemType { return IntegerT }
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.
func (i *BigIntegerItem) MarshalJSON() ([]byte, error) { func (i *BigIntegerItem) MarshalJSON() ([]byte, error) {
return json.Marshal(i.value) return json.Marshal(i.value)
@ -380,6 +391,9 @@ func (i *BoolItem) ToContractParameter(map[StackItem]bool) smartcontract.Paramet
} }
} }
// Type implements StackItem interface.
func (i *BoolItem) Type() StackItemType { return BooleanT }
// ByteArrayItem represents a byte array on the stack. // ByteArrayItem represents a byte array on the stack.
type ByteArrayItem struct { type ByteArrayItem struct {
value []byte value []byte
@ -442,6 +456,9 @@ func (i *ByteArrayItem) ToContractParameter(map[StackItem]bool) smartcontract.Pa
} }
} }
// Type implements StackItem interface.
func (i *ByteArrayItem) Type() StackItemType { return ByteArrayT }
// ArrayItem represents a new ArrayItem object. // ArrayItem represents a new ArrayItem object.
type ArrayItem struct { type ArrayItem struct {
value []StackItem value []StackItem
@ -506,6 +523,9 @@ func (i *ArrayItem) ToContractParameter(seen map[StackItem]bool) smartcontract.P
} }
} }
// Type implements StackItem interface.
func (i *ArrayItem) Type() StackItemType { return ArrayT }
// MapElement is a key-value pair of StackItems. // MapElement is a key-value pair of StackItems.
type MapElement struct { type MapElement struct {
Key StackItem Key StackItem
@ -591,6 +611,9 @@ func (i *MapItem) ToContractParameter(seen map[StackItem]bool) smartcontract.Par
} }
} }
// Type implements StackItem interface.
func (i *MapItem) Type() StackItemType { return MapT }
// Add adds key-value pair to the map. // Add adds key-value pair to the map.
func (i *MapItem) Add(key, value StackItem) { func (i *MapItem) Add(key, value StackItem) {
if !isValidMapKey(key) { if !isValidMapKey(key) {
@ -678,6 +701,9 @@ func (i *InteropItem) ToContractParameter(map[StackItem]bool) smartcontract.Para
} }
} }
// Type implements StackItem interface.
func (i *InteropItem) Type() StackItemType { return InteropT }
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.
func (i *InteropItem) MarshalJSON() ([]byte, error) { func (i *InteropItem) MarshalJSON() ([]byte, error) {
return json.Marshal(i.value) return json.Marshal(i.value)

View file

@ -565,6 +565,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
res := v.estack.Pop().value.Equals(NullItem{}) res := v.estack.Pop().value.Equals(NullItem{})
v.estack.PushVal(res) v.estack.PushVal(res)
case opcode.ISTYPE:
res := v.estack.Pop().Item()
v.estack.PushVal(res.Type() == StackItemType(parameter[0]))
// Stack operations. // Stack operations.
case opcode.TOALTSTACK: case opcode.TOALTSTACK:
v.astack.Push(v.estack.Pop()) v.astack.Push(v.estack.Pop())
@ -911,8 +915,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(x.Cmp(big.NewInt(0)) != 0) v.estack.PushVal(x.Cmp(big.NewInt(0)) != 0)
// Object operations. // Object operations
case opcode.NEWARRAY: case opcode.NEWARRAY0:
v.estack.PushVal(&ArrayItem{[]StackItem{}})
case opcode.NEWARRAY, opcode.NEWARRAYT:
item := v.estack.Pop() item := v.estack.Pop()
switch t := item.value.(type) { switch t := item.value.(type) {
case *StructItem: case *StructItem:
@ -926,10 +933,17 @@ 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")
} }
items := makeArrayOfFalses(int(n)) typ := BooleanT
if op == opcode.NEWARRAYT {
typ = StackItemType(parameter[0])
}
items := makeArrayOfType(int(n), typ)
v.estack.PushVal(&ArrayItem{items}) v.estack.PushVal(&ArrayItem{items})
} }
case opcode.NEWSTRUCT0:
v.estack.PushVal(&StructItem{[]StackItem{}})
case opcode.NEWSTRUCT: case opcode.NEWSTRUCT:
item := v.estack.Pop() item := v.estack.Pop()
switch t := item.value.(type) { switch t := item.value.(type) {
@ -944,7 +958,7 @@ 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 := makeArrayOfFalses(int(n)) items := makeArrayOfType(int(n), BooleanT)
v.estack.PushVal(&StructItem{items}) v.estack.PushVal(&StructItem{items})
} }
@ -1058,7 +1072,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic(fmt.Sprintf("SETITEM: invalid item type %s", t)) panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
} }
case opcode.REVERSE: case opcode.REVERSEITEMS:
a := v.estack.Pop().Array() a := v.estack.Pop().Array()
if len(a) > 1 { if len(a) > 1 {
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 {
@ -1100,7 +1114,29 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("REMOVE: invalid type") panic("REMOVE: invalid type")
} }
case opcode.ARRAYSIZE: case opcode.CLEARITEMS:
elem := v.estack.Pop()
switch t := elem.value.(type) {
case *ArrayItem:
for _, item := range t.value {
v.estack.updateSizeRemove(item)
}
t.value = t.value[:0]
case *StructItem:
for _, item := range t.value {
v.estack.updateSizeRemove(item)
}
t.value = t.value[:0]
case *MapItem:
for i := range t.value {
v.estack.updateSizeRemove(t.value[i].Value)
}
t.value = t.value[:0]
default:
panic("CLEARITEMS: invalid type")
}
case opcode.SIZE:
elem := v.estack.Pop() elem := v.estack.Pop()
// 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.
@ -1113,11 +1149,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushVal(len(elem.Bytes())) v.estack.PushVal(len(elem.Bytes()))
} }
case opcode.SIZE:
elem := v.estack.Pop()
arr := elem.Bytes()
v.estack.PushVal(len(arr))
case opcode.JMP, opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode.JMPIFNOTL, case opcode.JMP, opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode.JMPIFNOTL,
opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL, opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL,
opcode.JMPGT, opcode.JMPGTL, opcode.JMPGE, opcode.JMPGEL, opcode.JMPGT, opcode.JMPGTL, opcode.JMPGE, opcode.JMPGEL,
@ -1465,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) items := make([]StackItem, n)
for i := range items { 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 return items
} }

View file

@ -216,6 +216,46 @@ func TestISNULL(t *testing.T) {
}) })
} }
func testISTYPE(t *testing.T, result bool, typ StackItemType, item StackItem) {
prog := []byte{byte(opcode.ISTYPE), byte(typ)}
v := load(prog)
v.estack.PushVal(item)
runVM(t, v)
require.Equal(t, 1, v.estack.Len())
require.Equal(t, result, v.estack.Pop().Bool())
}
func TestISTYPE(t *testing.T) {
t.Run("Integer", func(t *testing.T) {
testISTYPE(t, true, IntegerT, NewBigIntegerItem(big.NewInt(42)))
testISTYPE(t, false, IntegerT, NewByteArrayItem([]byte{}))
})
t.Run("Boolean", func(t *testing.T) {
testISTYPE(t, true, BooleanT, NewBoolItem(true))
testISTYPE(t, false, BooleanT, NewByteArrayItem([]byte{}))
})
t.Run("ByteArray", func(t *testing.T) {
testISTYPE(t, true, ByteArrayT, NewByteArrayItem([]byte{}))
testISTYPE(t, false, ByteArrayT, NewBigIntegerItem(big.NewInt(42)))
})
t.Run("Array", func(t *testing.T) {
testISTYPE(t, true, ArrayT, NewArrayItem([]StackItem{}))
testISTYPE(t, false, ArrayT, NewByteArrayItem([]byte{}))
})
t.Run("Struct", func(t *testing.T) {
testISTYPE(t, true, StructT, NewStructItem([]StackItem{}))
testISTYPE(t, false, StructT, NewByteArrayItem([]byte{}))
})
t.Run("Map", func(t *testing.T) {
testISTYPE(t, true, MapT, NewMapItem())
testISTYPE(t, false, MapT, NewByteArrayItem([]byte{}))
})
t.Run("Interop", func(t *testing.T) {
testISTYPE(t, true, InteropT, NewInteropItem(42))
testISTYPE(t, false, InteropT, NewByteArrayItem([]byte{}))
})
}
// appendBigStruct returns a program which: // appendBigStruct returns a program which:
// 1. pushes size Structs on stack // 1. pushes size Structs on stack
// 2. packs them into a new struct // 2. packs them into a new struct
@ -620,7 +660,7 @@ func TestSerializeArray(t *testing.T) {
func TestSerializeArrayBad(t *testing.T) { func TestSerializeArrayBad(t *testing.T) {
vm := load(getSerializeProg()) vm := load(getSerializeProg())
item := NewArrayItem(makeArrayOfFalses(2)) item := NewArrayItem(makeArrayOfType(2, BooleanT))
item.value[1] = item item.value[1] = item
vm.estack.Push(&Element{value: item}) vm.estack.Push(&Element{value: item})
@ -685,7 +725,7 @@ func TestSerializeMap(t *testing.T) {
} }
func TestSerializeMapCompat(t *testing.T) { func TestSerializeMapCompat(t *testing.T) {
resHex := "820100036b6579000576616c7565" resHex := "480128036b6579280576616c7565"
res, err := hex.DecodeString(resHex) res, err := hex.DecodeString(resHex)
require.NoError(t, err) require.NoError(t, err)
@ -1252,6 +1292,22 @@ func TestDECBigResult(t *testing.T) {
checkVMFailed(t, vm) checkVMFailed(t, vm)
} }
func TestNEWARRAY0(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY0)
v := load(prog)
runVM(t, v)
require.Equal(t, 1, v.estack.Len())
require.Equal(t, &ArrayItem{[]StackItem{}}, v.estack.Pop().value)
}
func TestNEWSTRUCT0(t *testing.T) {
prog := makeProgram(opcode.NEWSTRUCT0)
v := load(prog)
runVM(t, v)
require.Equal(t, 1, v.estack.Len())
require.Equal(t, &StructItem{[]StackItem{}}, v.estack.Pop().value)
}
func TestNEWARRAYInteger(t *testing.T) { func TestNEWARRAYInteger(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY) prog := makeProgram(opcode.NEWARRAY)
vm := load(prog) vm := load(prog)
@ -1281,7 +1337,7 @@ func testNEWARRAYIssue437(t *testing.T, i1, i2 opcode.Opcode, appended bool) {
vm := load(prog) vm := load(prog)
vm.Run() vm.Run()
arr := makeArrayOfFalses(4) arr := makeArrayOfType(4, BooleanT)
arr[2] = makeStackItem(3) arr[2] = makeStackItem(3)
arr[3] = makeStackItem(4) arr[3] = makeStackItem(4)
if appended { if appended {
@ -1323,6 +1379,32 @@ func TestNEWARRAYByteArray(t *testing.T) {
assert.Equal(t, &ArrayItem{[]StackItem{}}, vm.estack.Pop().value) 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) { func TestNEWARRAYBadSize(t *testing.T) {
prog := makeProgram(opcode.NEWARRAY) prog := makeProgram(opcode.NEWARRAY)
vm := load(prog) vm := load(prog)
@ -1581,8 +1663,8 @@ func TestSIZEBool(t *testing.T) {
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value) assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
} }
func TestARRAYSIZEArray(t *testing.T) { func TestSIZEArray(t *testing.T) {
prog := makeProgram(opcode.ARRAYSIZE) prog := makeProgram(opcode.SIZE)
vm := load(prog) vm := load(prog)
vm.estack.PushVal([]StackItem{ vm.estack.PushVal([]StackItem{
makeStackItem(1), makeStackItem(1),
@ -1593,8 +1675,8 @@ func TestARRAYSIZEArray(t *testing.T) {
assert.Equal(t, makeStackItem(2), vm.estack.Pop().value) assert.Equal(t, makeStackItem(2), vm.estack.Pop().value)
} }
func TestARRAYSIZEMap(t *testing.T) { func TestSIZEMap(t *testing.T) {
prog := makeProgram(opcode.ARRAYSIZE) prog := makeProgram(opcode.SIZE)
vm := load(prog) vm := load(prog)
m := NewMapItem() m := NewMapItem()
@ -2534,19 +2616,19 @@ func TestUNPACKGood(t *testing.T) {
assert.Equal(t, int64(1), vm.estack.Peek(len(elements)+1).BigInt().Int64()) assert.Equal(t, int64(1), vm.estack.Peek(len(elements)+1).BigInt().Int64())
} }
func TestREVERSEBadNotArray(t *testing.T) { func TestREVERSEITEMSBadNotArray(t *testing.T) {
prog := makeProgram(opcode.REVERSE) prog := makeProgram(opcode.REVERSEITEMS)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(1) vm.estack.PushVal(1)
checkVMFailed(t, vm) checkVMFailed(t, vm)
} }
func testREVERSEIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) { func testREVERSEITEMSIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) {
prog := makeProgram( prog := makeProgram(
opcode.PUSH0, i1, opcode.PUSH0, i1,
opcode.DUP, opcode.PUSH1, opcode.APPEND, opcode.DUP, opcode.PUSH1, opcode.APPEND,
opcode.DUP, opcode.PUSH2, opcode.APPEND, opcode.DUP, opcode.PUSH2, opcode.APPEND,
opcode.DUP, i2, opcode.REVERSE) opcode.DUP, i2, opcode.REVERSEITEMS)
vm := load(prog) vm := load(prog)
vm.Run() vm.Run()
@ -2567,15 +2649,15 @@ func testREVERSEIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) {
} }
} }
func TestREVERSEIssue437(t *testing.T) { func TestREVERSEITEMSIssue437(t *testing.T) {
t.Run("Array+Array", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) }) t.Run("Array+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) })
t.Run("Struct+Struct", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) }) t.Run("Struct+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) })
t.Run("Array+Struct", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) }) t.Run("Array+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) })
t.Run("Struct+Array", func(t *testing.T) { testREVERSEIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) }) t.Run("Struct+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) })
} }
func TestREVERSEGoodOneElem(t *testing.T) { func TestREVERSEITEMSGoodOneElem(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.REVERSE) prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS)
elements := []int{22} elements := []int{22}
vm := load(prog) vm := load(prog)
vm.estack.PushVal(1) vm.estack.PushVal(1)
@ -2588,13 +2670,13 @@ func TestREVERSEGoodOneElem(t *testing.T) {
assert.Equal(t, int64(elements[0]), e.Int64()) assert.Equal(t, int64(elements[0]), e.Int64())
} }
func TestREVERSEGoodStruct(t *testing.T) { func TestREVERSEITEMSGoodStruct(t *testing.T) {
eodd := []int{22, 34, 42, 55, 81} eodd := []int{22, 34, 42, 55, 81}
even := []int{22, 34, 42, 55, 81, 99} even := []int{22, 34, 42, 55, 81, 99}
eall := [][]int{eodd, even} eall := [][]int{eodd, even}
for _, elements := range eall { for _, elements := range eall {
prog := makeProgram(opcode.DUP, opcode.REVERSE) prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(1) vm.estack.PushVal(1)
@ -2616,13 +2698,13 @@ func TestREVERSEGoodStruct(t *testing.T) {
} }
} }
func TestREVERSEGood(t *testing.T) { func TestREVERSEITEMSGood(t *testing.T) {
eodd := []int{22, 34, 42, 55, 81} eodd := []int{22, 34, 42, 55, 81}
even := []int{22, 34, 42, 55, 81, 99} even := []int{22, 34, 42, 55, 81, 99}
eall := [][]int{eodd, even} eall := [][]int{eodd, even}
for _, elements := range eall { for _, elements := range eall {
prog := makeProgram(opcode.DUP, opcode.REVERSE) prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(1) vm.estack.PushVal(1)
vm.estack.PushVal(elements) vm.estack.PushVal(elements)
@ -2697,6 +2779,43 @@ func TestREMOVEMap(t *testing.T) {
assert.Equal(t, makeStackItem(false), vm.estack.Pop().value) assert.Equal(t, makeStackItem(false), vm.estack.Pop().value)
} }
func testCLEARITEMS(t *testing.T, item StackItem) {
prog := makeProgram(opcode.DUP, opcode.DUP, opcode.CLEARITEMS, opcode.SIZE)
v := load(prog)
v.estack.PushVal(item)
runVM(t, v)
require.Equal(t, 2, v.estack.Len())
require.EqualValues(t, 2, v.size) // empty collection + it's size
require.EqualValues(t, 0, v.estack.Pop().BigInt().Int64())
}
func TestCLEARITEMS(t *testing.T) {
arr := []StackItem{NewBigIntegerItem(big.NewInt(1)), NewByteArrayItem([]byte{1})}
m := NewMapItem()
m.Add(NewBigIntegerItem(big.NewInt(1)), NewByteArrayItem([]byte{}))
m.Add(NewByteArrayItem([]byte{42}), NewBigIntegerItem(big.NewInt(2)))
testCases := map[string]StackItem{
"empty Array": NewArrayItem([]StackItem{}),
"filled Array": NewArrayItem(arr),
"empty Struct": NewStructItem([]StackItem{}),
"filled Struct": NewStructItem(arr),
"empty Map": NewMapItem(),
"filled Map": m,
}
for name, item := range testCases {
t.Run(name, func(t *testing.T) { testCLEARITEMS(t, item) })
}
t.Run("Integer", func(t *testing.T) {
prog := makeProgram(opcode.CLEARITEMS)
v := load(prog)
v.estack.PushVal(1)
checkVMFailed(t, v)
})
}
func TestSWAPGood(t *testing.T) { func TestSWAPGood(t *testing.T) {
prog := makeProgram(opcode.SWAP) prog := makeProgram(opcode.SWAP)
vm := load(prog) vm := load(prog)