github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/vm/vm_test.go (about) 1 package vm 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/hex" 7 "errors" 8 "fmt" 9 "math" 10 "math/big" 11 "math/rand" 12 "strings" 13 "testing" 14 15 "github.com/nspcc-dev/neo-go/internal/random" 16 "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" 17 "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" 18 "github.com/nspcc-dev/neo-go/pkg/io" 19 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 20 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 21 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 22 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 23 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 ) 27 28 func fooInteropHandler(v *VM, id uint32) error { 29 if id == interopnames.ToID([]byte("foo")) { 30 if !v.AddGas(1) { 31 return errors.New("invalid gas amount") 32 } 33 v.Estack().PushVal(1) 34 return nil 35 } 36 return errors.New("syscall not found") 37 } 38 39 func TestInteropHook(t *testing.T) { 40 v := newTestVM() 41 v.SyscallHandler = fooInteropHandler 42 43 buf := io.NewBufBinWriter() 44 emit.Syscall(buf.BinWriter, "foo") 45 emit.Opcodes(buf.BinWriter, opcode.RET) 46 v.Load(buf.Bytes()) 47 runVM(t, v) 48 assert.Equal(t, 1, v.estack.Len()) 49 assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value()) 50 } 51 52 func TestVM_SetPriceGetter(t *testing.T) { 53 v := newTestVM() 54 prog := []byte{ 55 byte(opcode.PUSH4), byte(opcode.PUSH2), 56 byte(opcode.PUSHDATA1), 0x01, 0x01, 57 byte(opcode.PUSHDATA1), 0x02, 0xCA, 0xFE, 58 byte(opcode.PUSH4), byte(opcode.RET), 59 } 60 61 t.Run("no price getter", func(t *testing.T) { 62 v.Load(prog) 63 runVM(t, v) 64 65 require.EqualValues(t, 0, v.GasConsumed()) 66 }) 67 68 v.SetPriceGetter(func(op opcode.Opcode, p []byte) int64 { 69 if op == opcode.PUSH4 { 70 return 1 71 } else if op == opcode.PUSHDATA1 && bytes.Equal(p, []byte{0xCA, 0xFE}) { 72 return 7 73 } 74 75 return 0 76 }) 77 78 t.Run("with price getter", func(t *testing.T) { 79 v.Load(prog) 80 runVM(t, v) 81 82 require.EqualValues(t, 9, v.GasConsumed()) 83 }) 84 85 t.Run("with sufficient gas limit", func(t *testing.T) { 86 v.Load(prog) 87 v.GasLimit = 9 88 runVM(t, v) 89 90 require.EqualValues(t, 9, v.GasConsumed()) 91 }) 92 93 t.Run("with small gas limit", func(t *testing.T) { 94 v.Load(prog) 95 v.GasLimit = 8 96 checkVMFailed(t, v) 97 }) 98 } 99 100 func TestAddGas(t *testing.T) { 101 v := newTestVM() 102 v.GasLimit = 10 103 require.True(t, v.AddGas(5)) 104 require.True(t, v.AddGas(5)) 105 require.False(t, v.AddGas(5)) 106 } 107 108 func TestPushBytes1to75(t *testing.T) { 109 buf := io.NewBufBinWriter() 110 for i := 1; i <= 75; i++ { 111 b := randomBytes(i) 112 emit.Bytes(buf.BinWriter, b) 113 vm := load(buf.Bytes()) 114 err := vm.Step() 115 require.NoError(t, err) 116 117 assert.Equal(t, 1, vm.estack.Len()) 118 119 elem := vm.estack.Pop() 120 assert.IsType(t, &stackitem.ByteArray{}, elem.value) 121 assert.IsType(t, elem.Bytes(), b) 122 assert.Equal(t, 0, vm.estack.Len()) 123 124 errExec := vm.execute(nil, opcode.RET, nil) 125 require.NoError(t, errExec) 126 127 assert.Nil(t, vm.Context()) 128 buf.Reset() 129 } 130 } 131 132 func runVM(t *testing.T, vm *VM) { 133 err := vm.Run() 134 require.NoError(t, err) 135 assert.Equal(t, false, vm.HasFailed()) 136 } 137 138 func checkVMFailed(t *testing.T, vm *VM) { 139 err := vm.Run() 140 require.Error(t, err) 141 assert.Equal(t, true, vm.HasFailed()) 142 } 143 144 func TestStackLimitPUSH1Good(t *testing.T) { 145 prog := make([]byte, MaxStackSize*2) 146 for i := 0; i < MaxStackSize; i++ { 147 prog[i] = byte(opcode.PUSH1) 148 } 149 for i := MaxStackSize; i < MaxStackSize*2; i++ { 150 prog[i] = byte(opcode.DROP) 151 } 152 153 v := load(prog) 154 runVM(t, v) 155 } 156 157 func TestStackLimitPUSH1Bad(t *testing.T) { 158 prog := make([]byte, MaxStackSize+1) 159 for i := range prog { 160 prog[i] = byte(opcode.PUSH1) 161 } 162 v := load(prog) 163 checkVMFailed(t, v) 164 } 165 166 func TestPUSHINT(t *testing.T) { 167 for i := byte(0); i < 5; i++ { 168 op := opcode.PUSHINT8 + opcode.Opcode(i) 169 t.Run(op.String(), func(t *testing.T) { 170 buf := random.Bytes((8 << i) / 8) 171 prog := append([]byte{byte(op)}, buf...) 172 runWithArgs(t, prog, bigint.FromBytes(buf)) 173 }) 174 } 175 } 176 177 func TestPUSHNULL(t *testing.T) { 178 prog := makeProgram(opcode.PUSHNULL, opcode.PUSHNULL, opcode.EQUAL) 179 v := load(prog) 180 require.NoError(t, v.Step()) 181 require.Equal(t, 1, v.estack.Len()) 182 runVM(t, v) 183 require.True(t, v.estack.Pop().Bool()) 184 } 185 186 func TestISNULL(t *testing.T) { 187 prog := makeProgram(opcode.ISNULL) 188 t.Run("Integer", getTestFuncForVM(prog, false, 1)) 189 t.Run("Null", getTestFuncForVM(prog, true, stackitem.Null{})) 190 } 191 192 func testISTYPE(t *testing.T, result bool, typ stackitem.Type, item stackitem.Item) { 193 prog := []byte{byte(opcode.ISTYPE), byte(typ)} 194 runWithArgs(t, prog, result, item) 195 } 196 197 func TestISTYPE(t *testing.T) { 198 t.Run("Integer", func(t *testing.T) { 199 testISTYPE(t, true, stackitem.IntegerT, stackitem.NewBigInteger(big.NewInt(42))) 200 testISTYPE(t, false, stackitem.IntegerT, stackitem.NewByteArray([]byte{})) 201 }) 202 t.Run("Boolean", func(t *testing.T) { 203 testISTYPE(t, true, stackitem.BooleanT, stackitem.NewBool(true)) 204 testISTYPE(t, false, stackitem.BooleanT, stackitem.NewByteArray([]byte{})) 205 }) 206 t.Run("ByteArray", func(t *testing.T) { 207 testISTYPE(t, true, stackitem.ByteArrayT, stackitem.NewByteArray([]byte{})) 208 testISTYPE(t, false, stackitem.ByteArrayT, stackitem.NewBigInteger(big.NewInt(42))) 209 }) 210 t.Run("Array", func(t *testing.T) { 211 testISTYPE(t, true, stackitem.ArrayT, stackitem.NewArray([]stackitem.Item{})) 212 testISTYPE(t, false, stackitem.ArrayT, stackitem.NewByteArray([]byte{})) 213 }) 214 t.Run("Struct", func(t *testing.T) { 215 testISTYPE(t, true, stackitem.StructT, stackitem.NewStruct([]stackitem.Item{})) 216 testISTYPE(t, false, stackitem.StructT, stackitem.NewByteArray([]byte{})) 217 }) 218 t.Run("Map", func(t *testing.T) { 219 testISTYPE(t, true, stackitem.MapT, stackitem.NewMap()) 220 testISTYPE(t, false, stackitem.MapT, stackitem.NewByteArray([]byte{})) 221 }) 222 t.Run("Interop", func(t *testing.T) { 223 testISTYPE(t, true, stackitem.InteropT, stackitem.NewInterop(42)) 224 testISTYPE(t, false, stackitem.InteropT, stackitem.NewByteArray([]byte{})) 225 }) 226 } 227 228 func testCONVERT(to stackitem.Type, item, res stackitem.Item) func(t *testing.T) { 229 return func(t *testing.T) { 230 prog := []byte{byte(opcode.CONVERT), byte(to)} 231 runWithArgs(t, prog, res, item) 232 } 233 } 234 235 func TestCONVERT(t *testing.T) { 236 type convertTC struct { 237 item, res stackitem.Item 238 } 239 arr := []stackitem.Item{ 240 stackitem.NewBigInteger(big.NewInt(7)), 241 stackitem.NewByteArray([]byte{4, 8, 15}), 242 } 243 m := stackitem.NewMap() 244 m.Add(stackitem.NewByteArray([]byte{1}), stackitem.NewByteArray([]byte{2})) 245 246 getName := func(item stackitem.Item, typ stackitem.Type) string { 247 return fmt.Sprintf("%s->%s", item, typ) 248 } 249 250 t.Run("->Bool", func(t *testing.T) { 251 testBool := func(a, b stackitem.Item) func(t *testing.T) { 252 return testCONVERT(stackitem.BooleanT, a, b) 253 } 254 255 trueCases := []stackitem.Item{ 256 stackitem.NewBool(true), stackitem.NewBigInteger(big.NewInt(11)), stackitem.NewByteArray([]byte{1, 2, 3}), 257 stackitem.NewArray(arr), stackitem.NewArray(nil), 258 stackitem.NewStruct(arr), stackitem.NewStruct(nil), 259 stackitem.NewMap(), m, stackitem.NewInterop(struct{}{}), 260 stackitem.NewPointer(0, []byte{}), 261 } 262 for i := range trueCases { 263 t.Run(getName(trueCases[i], stackitem.BooleanT), testBool(trueCases[i], stackitem.NewBool(true))) 264 } 265 266 falseCases := []stackitem.Item{ 267 stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewByteArray([]byte{0, 0}), stackitem.NewBool(false), 268 } 269 for i := range falseCases { 270 testBool(falseCases[i], stackitem.NewBool(false)) 271 } 272 }) 273 274 t.Run("compound/interop -> basic", func(t *testing.T) { 275 types := []stackitem.Type{stackitem.IntegerT, stackitem.ByteArrayT} 276 items := []stackitem.Item{stackitem.NewArray(nil), stackitem.NewStruct(nil), stackitem.NewMap(), stackitem.NewInterop(struct{}{})} 277 for _, typ := range types { 278 for j := range items { 279 t.Run(getName(items[j], typ), testCONVERT(typ, items[j], nil)) 280 } 281 } 282 }) 283 284 t.Run("primitive -> Integer/ByteArray", func(t *testing.T) { 285 n := big.NewInt(42) 286 b := bigint.ToBytes(n) 287 288 itemInt := stackitem.NewBigInteger(n) 289 itemBytes := stackitem.NewByteArray(b) 290 291 trueCases := map[stackitem.Type][]convertTC{ 292 stackitem.IntegerT: { 293 {itemInt, itemInt}, 294 {itemBytes, itemInt}, 295 {stackitem.NewBool(true), stackitem.NewBigInteger(big.NewInt(1))}, 296 {stackitem.NewBool(false), stackitem.NewBigInteger(big.NewInt(0))}, 297 }, 298 stackitem.ByteArrayT: { 299 {itemInt, itemBytes}, 300 {itemBytes, itemBytes}, 301 {stackitem.NewBool(true), stackitem.NewByteArray([]byte{1})}, 302 {stackitem.NewBool(false), stackitem.NewByteArray([]byte{0})}, 303 }, 304 } 305 306 for typ := range trueCases { 307 for _, tc := range trueCases[typ] { 308 t.Run(getName(tc.item, typ), testCONVERT(typ, tc.item, tc.res)) 309 } 310 } 311 }) 312 313 t.Run("Struct<->Array", func(t *testing.T) { 314 arrayItem := stackitem.NewArray(arr) 315 structItem := stackitem.NewStruct(arr) 316 t.Run("Array->Array", testCONVERT(stackitem.ArrayT, arrayItem, arrayItem)) 317 t.Run("Array->Struct", testCONVERT(stackitem.StructT, arrayItem, structItem)) 318 t.Run("Struct->Array", testCONVERT(stackitem.ArrayT, structItem, arrayItem)) 319 t.Run("Struct->Struct", testCONVERT(stackitem.StructT, structItem, structItem)) 320 }) 321 322 t.Run("Map->Map", testCONVERT(stackitem.MapT, m, m)) 323 324 ptr := stackitem.NewPointer(1, []byte{1}) 325 t.Run("Pointer->Pointer", testCONVERT(stackitem.PointerT, ptr, ptr)) 326 327 t.Run("Null->", func(t *testing.T) { 328 types := []stackitem.Type{ 329 stackitem.BooleanT, stackitem.ByteArrayT, stackitem.IntegerT, stackitem.ArrayT, stackitem.StructT, stackitem.MapT, stackitem.InteropT, stackitem.PointerT, 330 } 331 for i := range types { 332 t.Run(types[i].String(), testCONVERT(types[i], stackitem.Null{}, stackitem.Null{})) 333 } 334 }) 335 336 t.Run("->Any", func(t *testing.T) { 337 items := []stackitem.Item{ 338 stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewByteArray([]byte{1}), stackitem.NewBool(true), 339 stackitem.NewArray(arr), stackitem.NewStruct(arr), m, stackitem.NewInterop(struct{}{}), 340 } 341 342 for i := range items { 343 t.Run(items[i].String(), testCONVERT(stackitem.AnyT, items[i], nil)) 344 } 345 }) 346 } 347 348 // appendBigStruct returns a program which: 349 // 1. pushes size Structs on stack 350 // 2. packs them into a new struct 351 // 3. appends them to a zero-length array 352 // Resulting stack size consists of: 353 // - struct (size+1) 354 // - array (1) of struct (size+1) 355 // which equals to size*2+3 elements in total. 356 func appendBigStruct(size uint16) []opcode.Opcode { 357 prog := make([]opcode.Opcode, size*2) 358 for i := uint16(0); i < size; i++ { 359 prog[i*2] = opcode.PUSH0 360 prog[i*2+1] = opcode.NEWSTRUCT 361 } 362 363 return append(prog, 364 opcode.INITSSLOT, 1, 365 opcode.PUSHINT16, opcode.Opcode(size), opcode.Opcode(size>>8), // LE 366 opcode.PACK, opcode.CONVERT, opcode.Opcode(stackitem.StructT), 367 opcode.STSFLD0, opcode.LDSFLD0, 368 opcode.DUP, 369 opcode.PUSH0, opcode.NEWARRAY, 370 opcode.SWAP, 371 opcode.APPEND, opcode.RET) 372 } 373 374 func TestStackLimitAPPENDStructGood(t *testing.T) { 375 prog := makeProgram(appendBigStruct(MaxStackSize/2 - 2)...) 376 v := load(prog) 377 runVM(t, v) // size = 2047 = (Max/2-2)*2+3 = Max-1 378 } 379 380 func TestStackLimitAPPENDStructBad(t *testing.T) { 381 prog := makeProgram(appendBigStruct(MaxStackSize/2 - 1)...) 382 v := load(prog) 383 checkVMFailed(t, v) // size = 2049 = (Max/2-1)*2+3 = Max+1 384 } 385 386 func TestStackLimit(t *testing.T) { 387 expected := []struct { 388 inst opcode.Opcode 389 size int 390 }{ 391 {opcode.PUSH2, 2}, // 1 from INITSSLOT and 1 for integer 2 392 {opcode.NEWARRAY, 4}, // array + 2 items 393 {opcode.STSFLD0, 3}, 394 {opcode.LDSFLD0, 4}, 395 {opcode.NEWMAP, 5}, 396 {opcode.DUP, 6}, 397 {opcode.PUSH2, 7}, 398 {opcode.LDSFLD0, 8}, 399 {opcode.SETITEM, 7}, // -3 items and 1 new kv pair in map 400 {opcode.DUP, 8}, 401 {opcode.PUSH2, 9}, 402 {opcode.LDSFLD0, 10}, 403 {opcode.SETITEM, 7}, // -3 items and no new elements in map 404 {opcode.DUP, 8}, 405 {opcode.PUSH2, 9}, 406 {opcode.REMOVE, 5}, // as we have right after NEWMAP 407 {opcode.DROP, 4}, // DROP map with no elements 408 } 409 410 prog := make([]opcode.Opcode, len(expected)+2) 411 prog[0] = opcode.INITSSLOT 412 prog[1] = 1 413 for i := range expected { 414 prog[i+2] = expected[i].inst 415 } 416 417 vm := load(makeProgram(prog...)) 418 require.NoError(t, vm.Step(), "failed to initialize static slot") 419 for i := range expected { 420 require.NoError(t, vm.Step()) 421 require.Equal(t, expected[i].size, int(vm.refs), "i: %d", i) 422 } 423 } 424 425 func TestPushm1to16(t *testing.T) { 426 var prog []byte 427 for i := int(opcode.PUSHM1); i <= int(opcode.PUSH16); i++ { 428 if i == 80 { 429 continue // opcode layout we got here. 430 } 431 prog = append(prog, byte(i)) 432 } 433 434 vm := load(prog) 435 for i := int(opcode.PUSHM1); i <= int(opcode.PUSH16); i++ { 436 err := vm.Step() 437 require.NoError(t, err) 438 439 elem := vm.estack.Pop() 440 val := i - int(opcode.PUSH1) + 1 441 assert.Equal(t, elem.BigInt().Int64(), int64(val)) 442 } 443 } 444 445 func TestPUSHDATA1(t *testing.T) { 446 t.Run("Good", getTestFuncForVM([]byte{byte(opcode.PUSHDATA1), 3, 1, 2, 3}, []byte{1, 2, 3})) 447 t.Run("NoN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA1)}, nil)) 448 t.Run("BadN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA1), 1}, nil)) 449 } 450 451 func TestPUSHDATA2(t *testing.T) { 452 t.Run("Good", getTestFuncForVM([]byte{byte(opcode.PUSHDATA2), 3, 0, 1, 2, 3}, []byte{1, 2, 3})) 453 t.Run("NoN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA2)}, nil)) 454 t.Run("ShortN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA2), 0}, nil)) 455 t.Run("BadN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA2), 1, 0}, nil)) 456 } 457 458 func TestPUSHDATA4(t *testing.T) { 459 t.Run("Good", getTestFuncForVM([]byte{byte(opcode.PUSHDATA4), 3, 0, 0, 0, 1, 2, 3}, []byte{1, 2, 3})) 460 t.Run("NoN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA4)}, nil)) 461 t.Run("BadN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA4), 1, 0, 0, 0}, nil)) 462 t.Run("ShortN", getTestFuncForVM([]byte{byte(opcode.PUSHDATA4), 0, 0, 0}, nil)) 463 } 464 465 func TestPushData4BigN(t *testing.T) { 466 prog := make([]byte, 1+4+stackitem.MaxSize+1) 467 prog[0] = byte(opcode.PUSHDATA4) 468 binary.LittleEndian.PutUint32(prog[1:], stackitem.MaxSize+1) 469 470 vm := load(prog) 471 checkVMFailed(t, vm) 472 } 473 474 func getTestCallFlagsFunc(syscall []byte, flags callflag.CallFlag, result any) func(t *testing.T) { 475 return func(t *testing.T) { 476 script := append([]byte{byte(opcode.SYSCALL)}, syscall...) 477 v := newTestVM() 478 v.SyscallHandler = testSyscallHandler 479 v.LoadScriptWithFlags(script, flags) 480 if result == nil { 481 checkVMFailed(t, v) 482 return 483 } 484 runVM(t, v) 485 require.Equal(t, result, v.PopResult()) 486 } 487 } 488 489 func TestCallFlags(t *testing.T) { 490 noFlags := []byte{0x77, 0x77, 0x77, 0x77} 491 readOnly := []byte{0x66, 0x66, 0x66, 0x66} 492 t.Run("NoFlagsNoRequired", getTestCallFlagsFunc(noFlags, callflag.NoneFlag, new(int))) 493 t.Run("ProvideFlagsNoRequired", getTestCallFlagsFunc(noFlags, callflag.AllowCall, new(int))) 494 t.Run("NoFlagsSomeRequired", getTestCallFlagsFunc(readOnly, callflag.NoneFlag, nil)) 495 t.Run("OnlyOneProvided", getTestCallFlagsFunc(readOnly, callflag.AllowCall, nil)) 496 t.Run("AllFlagsProvided", getTestCallFlagsFunc(readOnly, callflag.ReadOnly, new(int))) 497 } 498 499 func callNTimes(n uint16) []byte { 500 return makeProgram( 501 opcode.PUSHINT16, opcode.Opcode(n), opcode.Opcode(n>>8), // little-endian 502 opcode.INITSSLOT, 1, 503 opcode.STSFLD0, opcode.LDSFLD0, 504 opcode.JMPIF, 0x3, opcode.RET, 505 opcode.LDSFLD0, opcode.DEC, 506 opcode.CALL, 0xF9) // -7 -> JMP to TOALTSTACK) 507 } 508 509 func TestInvocationLimitGood(t *testing.T) { 510 prog := callNTimes(MaxInvocationStackSize - 1) 511 v := load(prog) 512 runVM(t, v) 513 } 514 515 func TestInvocationLimitBad(t *testing.T) { 516 prog := callNTimes(MaxInvocationStackSize) 517 v := load(prog) 518 checkVMFailed(t, v) 519 } 520 521 func isLongJMP(op opcode.Opcode) bool { 522 return op == opcode.JMPL || op == opcode.JMPIFL || op == opcode.JMPIFNOTL || 523 op == opcode.JMPEQL || op == opcode.JMPNEL || 524 op == opcode.JMPGEL || op == opcode.JMPGTL || 525 op == opcode.JMPLEL || op == opcode.JMPLTL 526 } 527 528 func getJMPProgram(op opcode.Opcode) []byte { 529 prog := []byte{byte(op)} 530 if isLongJMP(op) { 531 prog = append(prog, 0x07, 0x00, 0x00, 0x00) 532 } else { 533 prog = append(prog, 0x04) 534 } 535 return append(prog, byte(opcode.PUSH1), byte(opcode.RET), byte(opcode.PUSH2), byte(opcode.RET)) 536 } 537 538 func testJMP(t *testing.T, op opcode.Opcode, res any, items ...any) { 539 prog := getJMPProgram(op) 540 v := load(prog) 541 for i := range items { 542 v.estack.PushVal(items[i]) 543 } 544 if res == nil { 545 checkVMFailed(t, v) 546 return 547 } 548 runVM(t, v) 549 require.EqualValues(t, res, v.estack.Pop().BigInt().Int64()) 550 } 551 552 func TestJMPs(t *testing.T) { 553 testCases := []struct { 554 name string 555 items []any 556 }{ 557 { 558 name: "no condition", 559 }, 560 { 561 name: "single item (true)", 562 items: []any{true}, 563 }, 564 { 565 name: "single item (false)", 566 items: []any{false}, 567 }, 568 { 569 name: "24 and 42", 570 items: []any{24, 42}, 571 }, 572 { 573 name: "42 and 24", 574 items: []any{42, 24}, 575 }, 576 { 577 name: "42 and 42", 578 items: []any{42, 42}, 579 }, 580 } 581 582 // 2 is true, 1 is false 583 results := map[opcode.Opcode][]any{ 584 opcode.JMP: {2, 2, 2, 2, 2, 2}, 585 opcode.JMPIF: {nil, 2, 1, 2, 2, 2}, 586 opcode.JMPIFNOT: {nil, 1, 2, 1, 1, 1}, 587 opcode.JMPEQ: {nil, nil, nil, 1, 1, 2}, 588 opcode.JMPNE: {nil, nil, nil, 2, 2, 1}, 589 opcode.JMPGE: {nil, nil, nil, 1, 2, 2}, 590 opcode.JMPGT: {nil, nil, nil, 1, 2, 1}, 591 opcode.JMPLE: {nil, nil, nil, 2, 1, 2}, 592 opcode.JMPLT: {nil, nil, nil, 2, 1, 1}, 593 } 594 595 for i, tc := range testCases { 596 i := i 597 t.Run(tc.name, func(t *testing.T) { 598 for op := opcode.JMP; op < opcode.JMPLEL; op++ { 599 resOp := op 600 if isLongJMP(op) { 601 resOp-- 602 } 603 t.Run(op.String(), func(t *testing.T) { 604 testJMP(t, op, results[resOp][i], tc.items...) 605 }) 606 } 607 }) 608 } 609 } 610 611 func TestPUSHA(t *testing.T) { 612 t.Run("Negative", getTestFuncForVM(makeProgram(opcode.PUSHA, 0xFF, 0xFF, 0xFF, 0xFF), nil)) 613 t.Run("TooBig", getTestFuncForVM(makeProgram(opcode.PUSHA, 10, 0, 0, 0), nil)) 614 t.Run("Good", func(t *testing.T) { 615 prog := makeProgram(opcode.NOP, opcode.PUSHA, 2, 0, 0, 0) 616 runWithArgs(t, prog, stackitem.NewPointer(3, prog)) 617 }) 618 } 619 620 func TestCALLA(t *testing.T) { 621 prog := makeProgram(opcode.CALLA, opcode.PUSH2, opcode.ADD, opcode.RET, opcode.PUSH3, opcode.RET) 622 t.Run("InvalidScript", getTestFuncForVM(prog, nil, stackitem.NewPointer(4, []byte{1}))) 623 t.Run("Good", getTestFuncForVM(prog, 5, stackitem.NewPointer(4, prog))) 624 } 625 626 func TestCALL(t *testing.T) { 627 prog := makeProgram( 628 opcode.CALL, 4, opcode.ADD, opcode.RET, 629 opcode.CALL, 3, opcode.RET, 630 opcode.PUSH1, opcode.PUSH2, opcode.RET) 631 runWithArgs(t, prog, 3) 632 } 633 634 func TestNOT(t *testing.T) { 635 prog := makeProgram(opcode.NOT) 636 t.Run("Bool", getTestFuncForVM(prog, true, false)) 637 t.Run("NonZeroInt", getTestFuncForVM(prog, false, 3)) 638 t.Run("Array", getTestFuncForVM(prog, false, []stackitem.Item{})) 639 t.Run("Struct", getTestFuncForVM(prog, false, stackitem.NewStruct([]stackitem.Item{}))) 640 t.Run("ByteArray0", getTestFuncForVM(prog, true, []byte{0, 0})) 641 t.Run("ByteArray1", getTestFuncForVM(prog, false, []byte{0, 1})) 642 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 643 t.Run("Buffer0", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{}))) 644 t.Run("Buffer1", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{1}))) 645 } 646 647 // getBigInt returns 2^a+b. 648 func getBigInt(a, b int64) *big.Int { 649 p := new(big.Int).Exp(big.NewInt(2), big.NewInt(a), nil) 650 p.Add(p, big.NewInt(b)) 651 return p 652 } 653 654 func TestArith(t *testing.T) { 655 // results for 5 and 2 656 testCases := map[opcode.Opcode]int{ 657 opcode.ADD: 7, 658 opcode.MUL: 10, 659 opcode.DIV: 2, 660 opcode.MOD: 1, 661 opcode.SUB: 3, 662 } 663 664 for op, res := range testCases { 665 t.Run(op.String(), getTestFuncForVM(makeProgram(op), res, 5, 2)) 666 } 667 } 668 669 func TestADDBigResult(t *testing.T) { 670 prog := makeProgram(opcode.ADD) 671 runWithArgs(t, prog, nil, getBigInt(stackitem.MaxBigIntegerSizeBits-1, -1), 1) // 0x7FFF... 672 } 673 674 func TestMULBigResult(t *testing.T) { 675 prog := makeProgram(opcode.MUL) 676 bi := getBigInt(stackitem.MaxBigIntegerSizeBits/2+1, 0) 677 runWithArgs(t, prog, nil, bi, bi) 678 } 679 680 func TestArithNegativeArguments(t *testing.T) { 681 runCase := func(op opcode.Opcode, p, q, result int64) func(t *testing.T) { 682 return getTestFuncForVM(makeProgram(op), result, p, q) 683 } 684 685 t.Run("DIV", func(t *testing.T) { 686 t.Run("positive/positive", runCase(opcode.DIV, 5, 2, 2)) 687 t.Run("positive/negative", runCase(opcode.DIV, 5, -2, -2)) 688 t.Run("negative/positive", runCase(opcode.DIV, -5, 2, -2)) 689 t.Run("negative/negative", runCase(opcode.DIV, -5, -2, 2)) 690 }) 691 692 t.Run("MOD", func(t *testing.T) { 693 t.Run("positive/positive", runCase(opcode.MOD, 5, 2, 1)) 694 t.Run("positive/negative", runCase(opcode.MOD, 5, -2, 1)) 695 t.Run("negative/positive", runCase(opcode.MOD, -5, 2, -1)) 696 t.Run("negative/negative", runCase(opcode.MOD, -5, -2, -1)) 697 }) 698 699 t.Run("SHR", func(t *testing.T) { 700 t.Run("positive/positive", runCase(opcode.SHR, 5, 2, 1)) 701 t.Run("negative/positive", runCase(opcode.SHR, -5, 2, -2)) 702 }) 703 704 t.Run("SHL", func(t *testing.T) { 705 t.Run("positive/positive", runCase(opcode.SHL, 5, 2, 20)) 706 t.Run("negative/positive", runCase(opcode.SHL, -5, 2, -20)) 707 }) 708 } 709 710 func TestSUBBigResult(t *testing.T) { 711 prog := makeProgram(opcode.SUB) 712 bi := getBigInt(stackitem.MaxBigIntegerSizeBits-1, -1) 713 runWithArgs(t, prog, new(big.Int).Sub(big.NewInt(-1), bi), -1, bi) 714 runWithArgs(t, prog, nil, -2, bi) 715 } 716 717 func TestPOW(t *testing.T) { 718 prog := makeProgram(opcode.POW) 719 t.Run("good, positive", getTestFuncForVM(prog, 9, 3, 2)) 720 t.Run("good, negative, even", getTestFuncForVM(prog, 4, -2, 2)) 721 t.Run("good, negative, odd", getTestFuncForVM(prog, -8, -2, 3)) 722 t.Run("zero", getTestFuncForVM(prog, 1, 3, 0)) 723 t.Run("negative exponent", getTestFuncForVM(prog, nil, 3, -1)) 724 t.Run("too big exponent", getTestFuncForVM(prog, nil, 1, maxSHLArg+1)) 725 } 726 727 func TestSQRT(t *testing.T) { 728 prog := makeProgram(opcode.SQRT) 729 t.Run("good, positive", getTestFuncForVM(prog, 3, 9)) 730 t.Run("good, round down", getTestFuncForVM(prog, 2, 8)) 731 t.Run("one", getTestFuncForVM(prog, 1, 1)) 732 t.Run("zero", getTestFuncForVM(prog, 0, 0)) 733 t.Run("negative value", getTestFuncForVM(prog, nil, -1)) 734 } 735 736 func TestMODMUL(t *testing.T) { 737 prog := makeProgram(opcode.MODMUL) 738 t.Run("bad, zero mod", getTestFuncForVM(prog, nil, 1, 2, 0)) 739 t.Run("good, positive base", getTestFuncForVM(prog, 2, 3, 4, 5)) 740 t.Run("good, zero base", getTestFuncForVM(prog, 0, 0, 4, 5)) 741 t.Run("good, negative base", getTestFuncForVM(prog, 3, -3, 4, 5)) 742 t.Run("good, positive base, negative mod", getTestFuncForVM(prog, 2, 3, 4, -5)) 743 t.Run("good, negative base, negative mod", getTestFuncForVM(prog, 3, -3, 4, -5)) 744 } 745 746 func TestMODPOW(t *testing.T) { 747 prog := makeProgram(opcode.MODPOW) 748 t.Run("good, positive base", getTestFuncForVM(prog, 1, 3, 4, 5)) 749 t.Run("good, negative base", getTestFuncForVM(prog, 2, -3, 5, 5)) 750 t.Run("good, positive base, negative mod", getTestFuncForVM(prog, 1, 3, 4, -5)) 751 t.Run("good, negative base, negative mod", getTestFuncForVM(prog, 2, -3, 5, -5)) 752 t.Run("bad, big negative exponent", getTestFuncForVM(prog, nil, 3, -2, 5)) 753 t.Run("bad, zero modulus", getTestFuncForVM(prog, nil, 3, 4, 0)) 754 755 t.Run("inverse compatibility", func(t *testing.T) { // Tests are taken from C# node. 756 t.Run("bad mod", getTestFuncForVM(prog, nil, 1, -1, 0)) 757 t.Run("bad mod", getTestFuncForVM(prog, nil, 1, -1, 1)) 758 t.Run("bad base", getTestFuncForVM(prog, nil, 0, -1, 0)) 759 t.Run("bad base", getTestFuncForVM(prog, nil, 0, -1, 1)) 760 t.Run("no inverse exists", getTestFuncForVM(prog, nil, math.MaxUint16, -1, math.MaxUint8)) 761 t.Run("good", getTestFuncForVM(prog, 52, 19, -1, 141)) 762 }) 763 } 764 765 func TestSHR(t *testing.T) { 766 prog := makeProgram(opcode.SHR) 767 t.Run("Good", getTestFuncForVM(prog, 1, 4, 2)) 768 t.Run("Zero", getTestFuncForVM(prog, []byte{0, 1}, []byte{0, 1}, 0)) 769 t.Run("Negative", getTestFuncForVM(prog, nil, 5, -1)) 770 t.Run("very big", getTestFuncForVM(prog, nil, 5, maxu64Plus(1))) 771 } 772 773 func TestSHL(t *testing.T) { 774 prog := makeProgram(opcode.SHL) 775 t.Run("Good", getTestFuncForVM(prog, 16, 4, 2)) 776 t.Run("Zero", getTestFuncForVM(prog, []byte{0, 1}, []byte{0, 1}, 0)) 777 t.Run("BigShift", getTestFuncForVM(prog, nil, 5, maxSHLArg+1)) 778 t.Run("BigResult", getTestFuncForVM(prog, nil, getBigInt(stackitem.MaxBigIntegerSizeBits/2, 0), stackitem.MaxBigIntegerSizeBits/2)) 779 t.Run("very big shift", getTestFuncForVM(prog, nil, 5, maxu64Plus(1))) 780 } 781 782 func TestArithNullArg(t *testing.T) { 783 for _, op := range []opcode.Opcode{opcode.LT, opcode.LE, opcode.GT, opcode.GE} { 784 prog := makeProgram(op) 785 t.Run(op.String(), func(t *testing.T) { 786 runWithArgs(t, prog, false, stackitem.Null{}, 0) 787 runWithArgs(t, prog, false, 0, stackitem.Null{}) 788 runWithArgs(t, prog, nil, stackitem.NewInterop(nil), 1) // also has `.Value() == nil` 789 }) 790 } 791 } 792 793 func TestLT(t *testing.T) { 794 prog := makeProgram(opcode.LT) 795 runWithArgs(t, prog, false, 4, 3) 796 } 797 798 func TestLE(t *testing.T) { 799 prog := makeProgram(opcode.LE) 800 runWithArgs(t, prog, true, 2, 3) 801 } 802 803 func TestGT(t *testing.T) { 804 prog := makeProgram(opcode.GT) 805 runWithArgs(t, prog, true, 9, 3) 806 } 807 808 func TestGE(t *testing.T) { 809 prog := makeProgram(opcode.GE) 810 runWithArgs(t, prog, true, 3, 3) 811 } 812 813 func TestDepth(t *testing.T) { 814 prog := makeProgram(opcode.DEPTH) 815 vm := load(prog) 816 vm.estack.PushVal(1) 817 vm.estack.PushVal(2) 818 vm.estack.PushVal(3) 819 runVM(t, vm) 820 assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64()) 821 } 822 823 type simpleEquatable struct { 824 ok bool 825 } 826 827 var _ = stackitem.Equatable(simpleEquatable{}) 828 829 func (e simpleEquatable) Equals(other stackitem.Equatable) bool { 830 _, ok := other.(simpleEquatable) 831 return ok && e.ok 832 } 833 834 func TestEQUALTrue(t *testing.T) { 835 prog := makeProgram(opcode.DUP, opcode.EQUAL) 836 t.Run("Array", getTestFuncForVM(prog, true, []stackitem.Item{})) 837 t.Run("Map", getTestFuncForVM(prog, true, stackitem.NewMap())) 838 t.Run("Buffer", getTestFuncForVM(prog, true, stackitem.NewBuffer([]byte{1, 2}))) 839 t.Run("Equatable", getTestFuncForVM(prog, true, stackitem.NewInterop(simpleEquatable{ok: true}))) 840 } 841 842 func TestEQUAL(t *testing.T) { 843 prog := makeProgram(opcode.EQUAL) 844 t.Run("NoArgs", getTestFuncForVM(prog, nil)) 845 t.Run("OneArgument", getTestFuncForVM(prog, nil, 1)) 846 t.Run("Integer", getTestFuncForVM(prog, true, 5, 5)) 847 t.Run("IntegerByteArray", getTestFuncForVM(prog, false, []byte{16}, 16)) 848 t.Run("BooleanInteger", getTestFuncForVM(prog, false, true, 1)) 849 t.Run("Map", getTestFuncForVM(prog, false, stackitem.NewMap(), stackitem.NewMap())) 850 t.Run("Array", getTestFuncForVM(prog, false, []stackitem.Item{}, []stackitem.Item{})) 851 t.Run("Buffer", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{42}), stackitem.NewBuffer([]byte{42}))) 852 t.Run("EquatableFalse", getTestFuncForVM(prog, false, stackitem.NewInterop(simpleEquatable{false}), stackitem.NewInterop(simpleEquatable{}))) 853 t.Run("EquatableTrue", getTestFuncForVM(prog, true, stackitem.NewInterop(simpleEquatable{true}), stackitem.NewInterop(simpleEquatable{}))) 854 } 855 856 func TestEQUALByteArrayWithLimit(t *testing.T) { 857 prog := makeProgram(opcode.EQUAL) 858 t.Run("fits limit, equal", func(t *testing.T) { 859 args := make([]stackitem.Item, 2) 860 for i := range args { 861 args[i] = stackitem.NewStruct([]stackitem.Item{ 862 stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2)), 863 stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2)), 864 }) 865 } 866 getTestFuncForVM(prog, true, args[0], args[1])(t) 867 }) 868 t.Run("exceeds limit", func(t *testing.T) { 869 args := make([]stackitem.Item, 2) 870 for i := range args { 871 args[i] = stackitem.NewStruct([]stackitem.Item{ 872 stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2+1)), 873 stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2)), 874 }) 875 } 876 getTestFuncForVM(prog, nil, args[0], args[1])(t) // should FAULT due to comparable limit exceeding 877 }) 878 t.Run("fits limit, second elements are not equal", func(t *testing.T) { 879 args := make([]stackitem.Item, 2) 880 for i := range args { 881 args[i] = stackitem.NewStruct([]stackitem.Item{ 882 stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize-1)), 883 stackitem.NewBuffer(make([]byte, 1)), 884 }) 885 } 886 getTestFuncForVM(prog, false, args[0], args[1])(t) // no limit is exceeded, but the second struct item is a Buffer. 887 }) 888 t.Run("fits limit, equal", func(t *testing.T) { 889 args := make([]stackitem.Item, 2) 890 buf := stackitem.NewBuffer(make([]byte, 100500)) // takes only 1 comparable unit despite its length 891 for i := range args { 892 args[i] = stackitem.NewStruct([]stackitem.Item{ 893 stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize-1)), 894 buf, 895 }) 896 } 897 getTestFuncForVM(prog, true, args[0], args[1])(t) // should HALT, because no limit exceeded 898 }) 899 t.Run("exceeds limit, equal", func(t *testing.T) { 900 args := make([]stackitem.Item, 2) 901 buf := stackitem.NewBuffer(make([]byte, 100500)) // takes only 1 comparable unit despite its length 902 for i := range args { 903 args[i] = stackitem.NewStruct([]stackitem.Item{ 904 stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize-1)), // MaxByteArrayComparableSize-1 comparable units 905 buf, // 1 comparable unit 906 buf, // 1 comparable unit 907 }) 908 } 909 getTestFuncForVM(prog, nil, args[0], args[1])(t) // should FAULT, because limit is exceeded: 910 }) 911 } 912 913 func runWithArgs(t *testing.T, prog []byte, result any, args ...any) { 914 getTestFuncForVM(prog, result, args...)(t) 915 } 916 917 func getCustomTestFuncForVM(prog []byte, check func(t *testing.T, v *VM), args ...any) func(t *testing.T) { 918 return func(t *testing.T) { 919 v := load(prog) 920 for i := range args { 921 v.estack.PushVal(args[i]) 922 } 923 if check == nil { 924 checkVMFailed(t, v) 925 return 926 } 927 runVM(t, v) 928 check(t, v) 929 } 930 } 931 932 func getTestFuncForVM(prog []byte, result any, args ...any) func(t *testing.T) { 933 var f func(t *testing.T, v *VM) 934 if result != nil { 935 f = func(t *testing.T, v *VM) { 936 require.Equal(t, 1, v.estack.Len()) 937 require.Equal(t, stackitem.Make(result).Value(), v.estack.Pop().Value()) 938 } 939 } 940 return getCustomTestFuncForVM(prog, f, args...) 941 } 942 943 func makeRETProgram(t *testing.T, argCount, localCount int) []byte { 944 require.True(t, argCount+localCount <= 255) 945 946 fProg := []opcode.Opcode{opcode.INITSLOT, opcode.Opcode(localCount), opcode.Opcode(argCount)} 947 for i := 0; i < localCount; i++ { 948 fProg = append(fProg, opcode.PUSH8, opcode.STLOC, opcode.Opcode(i)) 949 } 950 fProg = append(fProg, opcode.RET) 951 952 offset := uint32(len(fProg) + 5) 953 param := make([]byte, 4) 954 binary.LittleEndian.PutUint32(param, offset) 955 956 ops := []opcode.Opcode{ 957 opcode.INITSSLOT, 0x01, 958 opcode.PUSHA, 11, 0, 0, 0, 959 opcode.STSFLD0, 960 opcode.JMPL, opcode.Opcode(param[0]), opcode.Opcode(param[1]), opcode.Opcode(param[2]), opcode.Opcode(param[3]), 961 } 962 ops = append(ops, fProg...) 963 964 // execute func multiple times to ensure total reference count is less than max 965 callCount := MaxStackSize/(argCount+localCount) + 1 966 args := make([]opcode.Opcode, argCount) 967 for i := range args { 968 args[i] = opcode.PUSH7 969 } 970 for i := 0; i < callCount; i++ { 971 ops = append(ops, args...) 972 ops = append(ops, opcode.LDSFLD0, opcode.CALLA) 973 } 974 return makeProgram(ops...) 975 } 976 977 func TestRETReferenceClear(t *testing.T) { 978 // 42 is a canary 979 t.Run("Argument", getTestFuncForVM(makeRETProgram(t, 100, 0), 42, 42)) 980 t.Run("Local", getTestFuncForVM(makeRETProgram(t, 0, 100), 42, 42)) 981 } 982 983 func TestNOTEQUALByteArray(t *testing.T) { 984 prog := makeProgram(opcode.NOTEQUAL) 985 t.Run("True", getTestFuncForVM(prog, true, []byte{1, 2}, []byte{0, 1, 2})) 986 t.Run("False", getTestFuncForVM(prog, false, []byte{1, 2}, []byte{1, 2})) 987 } 988 989 func TestNUMEQUAL(t *testing.T) { 990 prog := makeProgram(opcode.NUMEQUAL) 991 t.Run("True", getTestFuncForVM(prog, true, 2, 2)) 992 t.Run("False", getTestFuncForVM(prog, false, 1, 2)) 993 } 994 995 func TestNUMNOTEQUAL(t *testing.T) { 996 prog := makeProgram(opcode.NUMNOTEQUAL) 997 t.Run("True", getTestFuncForVM(prog, true, 1, 2)) 998 t.Run("False", getTestFuncForVM(prog, false, 2, 2)) 999 } 1000 1001 func TestINC(t *testing.T) { 1002 prog := makeProgram(opcode.INC) 1003 runWithArgs(t, prog, 2, 1) 1004 } 1005 1006 func TestINCBigResult(t *testing.T) { 1007 prog := makeProgram(opcode.INC, opcode.INC) 1008 vm := load(prog) 1009 x := getBigInt(stackitem.MaxBigIntegerSizeBits-1, -2) 1010 vm.estack.PushVal(x) 1011 1012 require.NoError(t, vm.Step()) 1013 require.False(t, vm.HasFailed()) 1014 require.Equal(t, 1, vm.estack.Len()) 1015 require.Equal(t, new(big.Int).Add(x, big.NewInt(1)), vm.estack.Top().BigInt()) 1016 1017 checkVMFailed(t, vm) 1018 } 1019 1020 func TestDECBigResult(t *testing.T) { 1021 prog := makeProgram(opcode.DEC, opcode.DEC) 1022 vm := load(prog) 1023 x := getBigInt(stackitem.MaxBigIntegerSizeBits-1, -1) 1024 x.Neg(x) 1025 vm.estack.PushVal(x) 1026 1027 require.NoError(t, vm.Step()) 1028 require.False(t, vm.HasFailed()) 1029 require.Equal(t, 1, vm.estack.Len()) 1030 require.Equal(t, new(big.Int).Sub(x, big.NewInt(1)), vm.estack.Top().BigInt()) 1031 1032 checkVMFailed(t, vm) 1033 } 1034 1035 func TestNEWBUFFER(t *testing.T) { 1036 prog := makeProgram(opcode.NEWBUFFER) 1037 t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{0, 0, 0}), 3)) 1038 t.Run("Negative", getTestFuncForVM(prog, nil, -1)) 1039 t.Run("TooBig", getTestFuncForVM(prog, nil, stackitem.MaxSize+1)) 1040 } 1041 1042 func getTRYProgram(tryBlock, catchBlock, finallyBlock []byte) []byte { 1043 hasCatch := catchBlock != nil 1044 hasFinally := finallyBlock != nil 1045 tryOffset := len(tryBlock) + 3 + 2 // try args + endtry args 1046 1047 var ( 1048 catchLen, finallyLen int 1049 catchOffset, finallyOffset int 1050 ) 1051 if hasCatch { 1052 catchLen = len(catchBlock) + 2 // endtry args 1053 catchOffset = tryOffset 1054 } 1055 if hasFinally { 1056 finallyLen = len(finallyBlock) + 1 // endfinally 1057 finallyOffset = tryOffset + catchLen 1058 } 1059 prog := []byte{byte(opcode.TRY), byte(catchOffset), byte(finallyOffset)} 1060 prog = append(prog, tryBlock...) 1061 prog = append(prog, byte(opcode.ENDTRY), byte(catchLen+finallyLen+2)) 1062 if hasCatch { 1063 prog = append(prog, catchBlock...) 1064 prog = append(prog, byte(opcode.ENDTRY), byte(finallyLen+2)) 1065 } 1066 if hasFinally { 1067 prog = append(prog, finallyBlock...) 1068 prog = append(prog, byte(opcode.ENDFINALLY)) 1069 } 1070 prog = append(prog, byte(opcode.RET)) 1071 return prog 1072 } 1073 1074 func getTRYTestFunc(result any, tryBlock, catchBlock, finallyBlock []byte) func(t *testing.T) { 1075 return func(t *testing.T) { 1076 prog := getTRYProgram(tryBlock, catchBlock, finallyBlock) 1077 runWithArgs(t, prog, result) 1078 } 1079 } 1080 1081 func TestTRY(t *testing.T) { 1082 throw := []byte{byte(opcode.PUSH13), byte(opcode.THROW)} 1083 push1 := []byte{byte(opcode.PUSH1)} 1084 add5 := []byte{byte(opcode.PUSH5), byte(opcode.ADD)} 1085 add9 := []byte{byte(opcode.PUSH9), byte(opcode.ADD)} 1086 t.Run("NoCatch", func(t *testing.T) { 1087 t.Run("NoFinally", func(t *testing.T) { 1088 prog := getTRYProgram(push1, nil, nil) 1089 vm := load(prog) 1090 checkVMFailed(t, vm) 1091 }) 1092 t.Run("WithFinally", getTRYTestFunc(10, push1, nil, add9)) 1093 t.Run("Throw", getTRYTestFunc(nil, throw, nil, add9)) 1094 }) 1095 t.Run("WithCatch", func(t *testing.T) { 1096 t.Run("NoFinally", func(t *testing.T) { 1097 t.Run("Simple", getTRYTestFunc(1, push1, add5, nil)) 1098 t.Run("Throw", getTRYTestFunc(18, throw, add5, nil)) 1099 t.Run("Abort", getTRYTestFunc(nil, []byte{byte(opcode.ABORT)}, push1, nil)) 1100 t.Run("AbortMSG", getTRYTestFunc(nil, []byte{byte(opcode.PUSH1), byte(opcode.ABORTMSG)}, push1, nil)) 1101 t.Run("ThrowInCatch", getTRYTestFunc(nil, throw, throw, nil)) 1102 }) 1103 t.Run("WithFinally", func(t *testing.T) { 1104 t.Run("Simple", getTRYTestFunc(10, push1, add5, add9)) 1105 t.Run("Throw", getTRYTestFunc(27, throw, add5, add9)) 1106 }) 1107 }) 1108 t.Run("Nested", func(t *testing.T) { 1109 t.Run("ReThrowInTry", func(t *testing.T) { 1110 inner := getTRYProgram(throw, []byte{byte(opcode.THROW)}, nil) 1111 getTRYTestFunc(27, inner, add5, add9)(t) 1112 }) 1113 t.Run("ThrowInFinally", func(t *testing.T) { 1114 inner := getTRYProgram(throw, add5, []byte{byte(opcode.THROW)}) 1115 getTRYTestFunc(32, inner, add5, add9)(t) 1116 }) 1117 t.Run("TryMaxDepth", func(t *testing.T) { 1118 loopTries := []byte{byte(opcode.INITSLOT), 0x01, 0x00, 1119 byte(opcode.PUSH16), byte(opcode.INC), byte(opcode.STLOC0), 1120 byte(opcode.TRY), 1, 1, // jump target 1121 byte(opcode.LDLOC0), byte(opcode.DEC), byte(opcode.DUP), 1122 byte(opcode.STLOC0), byte(opcode.PUSH0), 1123 byte(opcode.JMPGT), 0xf8, byte(opcode.LDLOC0)} 1124 vm := load(loopTries) 1125 checkVMFailed(t, vm) 1126 }) 1127 }) 1128 t.Run("ThrowInCall", func(t *testing.T) { 1129 catchP := []byte{byte(opcode.CALL), 2, byte(opcode.PUSH1), byte(opcode.ADD), byte(opcode.THROW), byte(opcode.RET)} 1130 inner := getTRYProgram(throw, catchP, []byte{byte(opcode.PUSH2)}) 1131 // add 5 to the exception, mul to the result of inner finally (2) 1132 getTRYTestFunc(47, inner, append(add5, byte(opcode.MUL)), add9)(t) 1133 }) 1134 t.Run("nested, in throw and catch in call", func(t *testing.T) { 1135 catchP := []byte{byte(opcode.PUSH10), byte(opcode.ADD)} 1136 inner := getTRYProgram(throw, catchP, []byte{byte(opcode.PUSH2)}) 1137 outer := getTRYProgram([]byte{byte(opcode.CALL), 0}, []byte{byte(opcode.PUSH3)}, []byte{byte(opcode.PUSH4)}) 1138 outer = append(outer, byte(opcode.RET)) 1139 outer[4] = byte(len(outer) - 3) // CALL argument at 3 (TRY) + 1 (CALL opcode) 1140 outer = append(outer, inner...) 1141 outer = append(outer, byte(opcode.RET)) 1142 1143 v := load(outer) 1144 runVM(t, v) 1145 require.Equal(t, 3, v.Estack().Len()) 1146 require.Equal(t, big.NewInt(4), v.Estack().Pop().Value()) // outer FINALLY 1147 require.Equal(t, big.NewInt(2), v.Estack().Pop().Value()) // inner FINALLY 1148 require.Equal(t, big.NewInt(23), v.Estack().Pop().Value()) // inner THROW + CATCH 1149 }) 1150 } 1151 1152 func TestMEMCPY(t *testing.T) { 1153 prog := makeProgram(opcode.MEMCPY) 1154 t.Run("Good", func(t *testing.T) { 1155 buf := stackitem.NewBuffer([]byte{0, 1, 2, 3}) 1156 runWithArgs(t, prog, stackitem.NewBuffer([]byte{0, 6, 7, 3}), buf, buf, 1, []byte{4, 5, 6, 7}, 2, 2) 1157 }) 1158 t.Run("NonZeroDstIndex", func(t *testing.T) { 1159 buf := stackitem.NewBuffer([]byte{0, 1, 2}) 1160 runWithArgs(t, prog, stackitem.NewBuffer([]byte{0, 6, 7}), buf, buf, 1, []byte{4, 5, 6, 7}, 2, 2) 1161 }) 1162 t.Run("NegativeSize", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{0, 1}), 0, []byte{2}, 0, -1)) 1163 t.Run("NegativeSrcIndex", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{0, 1}), 0, []byte{2}, -1, 1)) 1164 t.Run("NegativeDstIndex", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{0, 1}), -1, []byte{2}, 0, 1)) 1165 t.Run("BigSizeSrc", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{0, 1}), 0, []byte{2}, 0, 2)) 1166 t.Run("BigSizeDst", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{0, 1}), 0, []byte{2, 3, 4}, 0, 3)) 1167 } 1168 1169 func TestNEWARRAY0(t *testing.T) { 1170 prog := makeProgram(opcode.NEWARRAY0) 1171 runWithArgs(t, prog, []stackitem.Item{}) 1172 } 1173 1174 func TestNEWSTRUCT0(t *testing.T) { 1175 prog := makeProgram(opcode.NEWSTRUCT0) 1176 runWithArgs(t, prog, stackitem.NewStruct([]stackitem.Item{})) 1177 } 1178 1179 func TestNEWARRAYArray(t *testing.T) { 1180 prog := makeProgram(opcode.NEWARRAY) 1181 t.Run("ByteArray", getTestFuncForVM(prog, stackitem.NewArray([]stackitem.Item{}), []byte{})) 1182 t.Run("BadSize", getTestFuncForVM(prog, nil, MaxStackSize+1)) 1183 t.Run("Integer", getTestFuncForVM(prog, []stackitem.Item{stackitem.Null{}}, 1)) 1184 } 1185 1186 func testNEWARRAYIssue437(t *testing.T, i1 opcode.Opcode, t2 stackitem.Type, appended bool) { 1187 prog := makeProgram( 1188 opcode.PUSH2, i1, 1189 opcode.DUP, opcode.PUSH3, opcode.APPEND, 1190 opcode.INITSSLOT, 1, 1191 opcode.STSFLD0, opcode.LDSFLD0, opcode.CONVERT, opcode.Opcode(t2), 1192 opcode.DUP, opcode.PUSH4, opcode.APPEND, 1193 opcode.LDSFLD0, opcode.PUSH5, opcode.APPEND) 1194 1195 arr := makeArrayOfType(4, stackitem.AnyT) 1196 arr[2] = stackitem.Make(3) 1197 arr[3] = stackitem.Make(4) 1198 if appended { 1199 arr = append(arr, stackitem.Make(5)) 1200 } 1201 1202 if t2 == stackitem.ArrayT { 1203 runWithArgs(t, prog, stackitem.NewArray(arr)) 1204 } else { 1205 runWithArgs(t, prog, stackitem.NewStruct(arr)) 1206 } 1207 } 1208 1209 func TestNEWARRAYIssue437(t *testing.T) { 1210 t.Run("Array+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, stackitem.ArrayT, true) }) 1211 t.Run("Struct+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, stackitem.StructT, true) }) 1212 t.Run("Array+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, stackitem.StructT, false) }) 1213 t.Run("Struct+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, stackitem.ArrayT, false) }) 1214 } 1215 1216 func TestNEWARRAYT(t *testing.T) { 1217 testCases := map[stackitem.Type]stackitem.Item{ 1218 stackitem.BooleanT: stackitem.NewBool(false), 1219 stackitem.IntegerT: stackitem.NewBigInteger(big.NewInt(0)), 1220 stackitem.ByteArrayT: stackitem.NewByteArray([]byte{}), 1221 stackitem.ArrayT: stackitem.Null{}, 1222 0xFF: nil, 1223 } 1224 for typ, item := range testCases { 1225 prog := makeProgram(opcode.NEWARRAYT, opcode.Opcode(typ), opcode.PUSH0, opcode.PICKITEM) 1226 t.Run(typ.String(), getTestFuncForVM(prog, item, 1)) 1227 } 1228 } 1229 1230 func TestNEWSTRUCT(t *testing.T) { 1231 prog := makeProgram(opcode.NEWSTRUCT) 1232 t.Run("ByteArray", getTestFuncForVM(prog, stackitem.NewStruct([]stackitem.Item{}), []byte{})) 1233 t.Run("BadSize", getTestFuncForVM(prog, nil, MaxStackSize+1)) 1234 t.Run("Integer", getTestFuncForVM(prog, stackitem.NewStruct([]stackitem.Item{stackitem.Null{}}), 1)) 1235 } 1236 1237 func TestAPPEND(t *testing.T) { 1238 prog := makeProgram(opcode.DUP, opcode.PUSH5, opcode.APPEND) 1239 arr := []stackitem.Item{stackitem.Make(5)} 1240 t.Run("Array", getTestFuncForVM(prog, stackitem.NewArray(arr), stackitem.NewArray(nil))) 1241 t.Run("Struct", getTestFuncForVM(prog, stackitem.NewStruct(arr), stackitem.NewStruct(nil))) 1242 } 1243 1244 func TestAPPENDCloneStruct(t *testing.T) { 1245 prog := makeProgram(opcode.DUP, opcode.PUSH0, opcode.NEWSTRUCT, opcode.INITSSLOT, 1, opcode.STSFLD0, 1246 opcode.LDSFLD0, opcode.APPEND, opcode.LDSFLD0, opcode.PUSH1, opcode.APPEND) 1247 arr := []stackitem.Item{stackitem.NewStruct([]stackitem.Item{})} 1248 runWithArgs(t, prog, stackitem.NewArray(arr), stackitem.NewArray(nil)) 1249 } 1250 1251 func TestAPPENDBad(t *testing.T) { 1252 prog := makeProgram(opcode.APPEND) 1253 t.Run("no arguments", getTestFuncForVM(prog, nil)) 1254 t.Run("one argument", getTestFuncForVM(prog, nil, 1)) 1255 t.Run("wrong type", getTestFuncForVM(prog, nil, []byte{}, 1)) 1256 } 1257 1258 func TestAPPENDGoodSizeLimit(t *testing.T) { 1259 prog := makeProgram(opcode.NEWARRAY, opcode.DUP, opcode.PUSH0, opcode.APPEND) 1260 vm := load(prog) 1261 vm.estack.PushVal(MaxStackSize - 3) // 1 for array, 1 for copy, 1 for pushed 0. 1262 runVM(t, vm) 1263 assert.Equal(t, 1, vm.estack.Len()) 1264 assert.Equal(t, MaxStackSize-2, len(vm.estack.Pop().Array())) 1265 } 1266 1267 func TestAPPENDBadSizeLimit(t *testing.T) { 1268 prog := makeProgram(opcode.NEWARRAY, opcode.DUP, opcode.PUSH0, opcode.APPEND) 1269 runWithArgs(t, prog, nil, MaxStackSize) 1270 } 1271 1272 func TestAPPENDRefSizeLimit(t *testing.T) { 1273 prog := makeProgram(opcode.NEWARRAY0, opcode.DUP, opcode.DUP, opcode.APPEND, opcode.JMP, 0xfd) 1274 runWithArgs(t, prog, nil) 1275 } 1276 1277 func TestPICKITEM(t *testing.T) { 1278 prog := makeProgram(opcode.PICKITEM) 1279 t.Run("bad index", getTestFuncForVM(prog, nil, []stackitem.Item{}, 0)) 1280 t.Run("Array", getTestFuncForVM(prog, 2, []stackitem.Item{stackitem.Make(1), stackitem.Make(2)}, 1)) 1281 t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{1, 2}, 1)) 1282 t.Run("Buffer", getTestFuncForVM(prog, 2, stackitem.NewBuffer([]byte{1, 2}), 1)) 1283 t.Run("Exceptions", func(t *testing.T) { 1284 tryProg := getTRYProgram( 1285 []byte{byte(opcode.PICKITEM), byte(opcode.RET)}, 1286 []byte{byte(opcode.RET)}, nil) 1287 items := []stackitem.Item{ 1288 stackitem.NewArray([]stackitem.Item{}), 1289 stackitem.NewBuffer([]byte{}), 1290 stackitem.NewByteArray([]byte{}), 1291 } 1292 for _, item := range items { 1293 t.Run(item.String()+", negative", getTestFuncForVM(tryProg, 1294 fmt.Sprintf("The value %d is out of range.", math.MinInt32), 1295 item, math.MinInt32)) 1296 t.Run(item.String()+", very big index", 1297 getTestFuncForVM(tryProg, nil, item, int64(math.MaxInt32)+1)) 1298 } 1299 1300 m := stackitem.NewMap() 1301 t.Run("Map, missing key", getTestFuncForVM(tryProg, "Key not found in Map", m, 1)) 1302 }) 1303 } 1304 1305 func TestVMPrintOps(t *testing.T) { 1306 w := io.NewBufBinWriter() 1307 emit.Bytes(w.BinWriter, make([]byte, 1000)) 1308 emit.Opcodes(w.BinWriter, opcode.PUSH0) 1309 emit.Bytes(w.BinWriter, make([]byte, 8)) 1310 1311 buf := bytes.NewBuffer(nil) 1312 v := New() 1313 v.Load(w.Bytes()) 1314 v.PrintOps(buf) 1315 1316 ss := strings.Split(buf.String(), "\n") 1317 require.Equal(t, 5, len(ss)) // header + 3 opcodes + trailing newline 1318 require.True(t, len(ss[0]) < 1000) 1319 require.True(t, len(ss[1]) > 1000) 1320 require.True(t, len(ss[2]) < 1000) 1321 require.True(t, len(ss[3]) < 1000) 1322 } 1323 1324 func TestPICKITEMDupArray(t *testing.T) { 1325 prog := makeProgram(opcode.DUP, opcode.PUSH0, opcode.PICKITEM, opcode.ABS) 1326 vm := load(prog) 1327 vm.estack.PushVal([]stackitem.Item{stackitem.Make(-1)}) 1328 runVM(t, vm) 1329 assert.Equal(t, 2, vm.estack.Len()) 1330 assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) 1331 items := vm.estack.Pop().Value().([]stackitem.Item) 1332 assert.Equal(t, big.NewInt(-1), items[0].Value()) 1333 } 1334 1335 func TestPICKITEMDupMap(t *testing.T) { 1336 prog := makeProgram(opcode.DUP, opcode.PUSHINT8, 42, opcode.PICKITEM, opcode.ABS) 1337 vm := load(prog) 1338 m := stackitem.NewMap() 1339 m.Add(stackitem.Make(42), stackitem.Make(-1)) 1340 vm.estack.Push(Element{value: m}) 1341 runVM(t, vm) 1342 assert.Equal(t, 2, vm.estack.Len()) 1343 assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) 1344 items := vm.estack.Pop().Value().([]stackitem.MapElement) 1345 assert.Equal(t, 1, len(items)) 1346 assert.Equal(t, big.NewInt(42), items[0].Key.Value()) 1347 assert.Equal(t, big.NewInt(-1), items[0].Value.Value()) 1348 } 1349 1350 func TestPICKITEMMap(t *testing.T) { 1351 prog := makeProgram(opcode.PICKITEM) 1352 m := stackitem.NewMap() 1353 m.Add(stackitem.Make(5), stackitem.Make(3)) 1354 runWithArgs(t, prog, 3, m, 5) 1355 } 1356 1357 func TestSETITEMBuffer(t *testing.T) { 1358 prog := makeProgram(opcode.DUP, opcode.REVERSE4, opcode.SETITEM) 1359 t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{0, 42, 2}), 42, 1, stackitem.NewBuffer([]byte{0, 1, 2}))) 1360 t.Run("BadValue", getTestFuncForVM(prog, nil, 256, 1, stackitem.NewBuffer([]byte{0, 1, 2}))) 1361 t.Run("Exceptions", func(t *testing.T) { 1362 tryProg := getTRYProgram( 1363 []byte{byte(opcode.SETITEM), byte(opcode.PUSH12), byte(opcode.RET)}, 1364 []byte{byte(opcode.RET)}, nil) 1365 t.Run("negative index", getTestFuncForVM(tryProg, 1366 fmt.Sprintf("The value %d is out of range.", math.MinInt32), 1367 stackitem.NewBuffer([]byte{0, 1, 2}), math.MinInt32, 0)) 1368 t.Run("very big index", getTestFuncForVM(tryProg, 1369 nil, stackitem.NewBuffer([]byte{0, 1, 2}), int64(math.MaxInt32)+1, 0)) 1370 }) 1371 } 1372 1373 func TestSETITEMArray(t *testing.T) { 1374 tryProg := getTRYProgram( 1375 []byte{byte(opcode.SETITEM), byte(opcode.RET)}, 1376 []byte{byte(opcode.RET)}, nil) 1377 t.Run("Good", func(t *testing.T) { 1378 arr := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(2)}) 1379 expected := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(42)}) 1380 runWithArgs(t, tryProg, expected, arr, arr, 1, 42) 1381 }) 1382 t.Run("negative index", getTestFuncForVM(tryProg, 1383 fmt.Sprintf("The value %d is out of range.", math.MinInt32), 1384 []stackitem.Item{}, math.MinInt32, 42)) 1385 t.Run("very big index", getTestFuncForVM(tryProg, 1386 nil, []stackitem.Item{}, int64(math.MaxInt32)+1, 0)) 1387 } 1388 1389 func TestSETITEMMap(t *testing.T) { 1390 prog := makeProgram(opcode.SETITEM, opcode.PICKITEM) 1391 m := stackitem.NewMap() 1392 m.Add(stackitem.Make(5), stackitem.Make(3)) 1393 runWithArgs(t, prog, []byte{0, 1}, m, 5, m, 5, []byte{0, 1}) 1394 1395 t.Run("big key", func(t *testing.T) { 1396 m := stackitem.NewMap() 1397 key := make([]byte, stackitem.MaxKeySize) 1398 for i := range key { 1399 key[i] = 0x0F 1400 } 1401 m.Add(stackitem.NewByteArray(key), stackitem.Make(3)) 1402 runWithArgs(t, prog, "value", m, key, m, key, "value") 1403 }) 1404 } 1405 1406 func TestSETITEMBigMapBad(t *testing.T) { 1407 prog := makeProgram(opcode.SETITEM) 1408 m := stackitem.NewMap() 1409 for i := 0; i < MaxStackSize; i++ { 1410 m.Add(stackitem.Make(i), stackitem.Make(i)) 1411 } 1412 1413 runWithArgs(t, prog, nil, m, m, MaxStackSize, 0) 1414 } 1415 1416 // This test checks is SETITEM properly updates reference counter. 1417 // 1. Create 2 arrays of size MaxStackSize/2 - 3. 1418 // 2. SETITEM each of them to a map. 1419 // 3. Replace each of them with a scalar value. 1420 func TestSETITEMMapStackLimit(t *testing.T) { 1421 size := MaxStackSize/2 - 4 1422 m := stackitem.NewMap() 1423 m.Add(stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) 1424 m.Add(stackitem.NewBigInteger(big.NewInt(2)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) 1425 1426 prog := makeProgram( 1427 opcode.DUP, opcode.PUSH1, opcode.PUSH1, opcode.SETITEM, 1428 opcode.DUP, opcode.PUSH2, opcode.PUSH2, opcode.SETITEM, 1429 opcode.DUP, opcode.PUSH3, opcode.PUSH3, opcode.SETITEM, 1430 opcode.DUP, opcode.PUSH4, opcode.PUSH4, opcode.SETITEM) 1431 v := load(prog) 1432 v.estack.PushVal(m) 1433 runVM(t, v) 1434 } 1435 1436 func TestSETITEMBigMapGood(t *testing.T) { 1437 prog := makeProgram(opcode.SETITEM) 1438 vm := load(prog) 1439 1440 m := stackitem.NewMap() 1441 for i := 0; i < MaxStackSize-3; i++ { 1442 m.Add(stackitem.Make(i), stackitem.Make(i)) 1443 } 1444 vm.estack.Push(Element{value: m}) 1445 vm.estack.PushVal(0) 1446 vm.estack.PushVal(0) 1447 1448 runVM(t, vm) 1449 } 1450 1451 func TestSIZE(t *testing.T) { 1452 prog := makeProgram(opcode.SIZE) 1453 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1454 t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{0, 1})) 1455 t.Run("Buffer", getTestFuncForVM(prog, 2, stackitem.NewBuffer([]byte{0, 1}))) 1456 t.Run("Bool", getTestFuncForVM(prog, 1, false)) 1457 t.Run("Array", getTestFuncForVM(prog, 2, []stackitem.Item{stackitem.Make(1), stackitem.Make([]byte{})})) 1458 t.Run("Map", func(t *testing.T) { 1459 m := stackitem.NewMap() 1460 m.Add(stackitem.Make(5), stackitem.Make(6)) 1461 m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make(6)) 1462 runWithArgs(t, prog, 2, m) 1463 }) 1464 } 1465 1466 func TestKEYSMap(t *testing.T) { 1467 prog := makeProgram(opcode.KEYS) 1468 vm := load(prog) 1469 1470 m := stackitem.NewMap() 1471 m.Add(stackitem.Make(5), stackitem.Make(6)) 1472 m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make(6)) 1473 vm.estack.Push(Element{value: m}) 1474 1475 runVM(t, vm) 1476 assert.Equal(t, 1, vm.estack.Len()) 1477 1478 top := vm.estack.Pop().value.(*stackitem.Array) 1479 assert.Equal(t, 2, len(top.Value().([]stackitem.Item))) 1480 assert.Contains(t, top.Value().([]stackitem.Item), stackitem.Make(5)) 1481 assert.Contains(t, top.Value().([]stackitem.Item), stackitem.Make([]byte{0, 1})) 1482 } 1483 1484 func TestKEYS(t *testing.T) { 1485 prog := makeProgram(opcode.KEYS) 1486 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1487 t.Run("WrongType", getTestFuncForVM(prog, nil, []stackitem.Item{})) 1488 } 1489 1490 func TestTry_ENDFINALLY_before_ENDTRY(t *testing.T) { 1491 prog := makeProgram(opcode.TRY, 0, 3, opcode.ENDFINALLY) 1492 require.NoError(t, IsScriptCorrect(prog, nil)) 1493 1494 v := load(prog) 1495 1496 var err error 1497 require.NotPanics(t, func() { err = v.Run() }) 1498 require.Error(t, err) 1499 } 1500 1501 func TestVALUESMap(t *testing.T) { 1502 prog := makeProgram(opcode.VALUES) 1503 vm := load(prog) 1504 1505 m := stackitem.NewMap() 1506 m.Add(stackitem.Make(5), stackitem.Make([]byte{2, 3})) 1507 m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make([]stackitem.Item{})) 1508 vm.estack.Push(Element{value: m}) 1509 1510 runVM(t, vm) 1511 assert.Equal(t, 1, vm.estack.Len()) 1512 1513 top := vm.estack.Pop().value.(*stackitem.Array) 1514 assert.Equal(t, 2, len(top.Value().([]stackitem.Item))) 1515 assert.Contains(t, top.Value().([]stackitem.Item), stackitem.Make([]byte{2, 3})) 1516 assert.Contains(t, top.Value().([]stackitem.Item), stackitem.Make([]stackitem.Item{})) 1517 } 1518 1519 func TestVALUES(t *testing.T) { 1520 prog := makeProgram(opcode.VALUES) 1521 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1522 t.Run("WrongType", getTestFuncForVM(prog, nil, 5)) 1523 t.Run("Array", getTestFuncForVM(prog, []int{4}, []int{4})) 1524 } 1525 1526 func TestHASKEY(t *testing.T) { 1527 prog := makeProgram(opcode.HASKEY) 1528 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1529 t.Run("OneArgument", getTestFuncForVM(prog, nil, 1)) 1530 t.Run("WrongKeyType", getTestFuncForVM(prog, nil, []stackitem.Item{}, []stackitem.Item{})) 1531 t.Run("WrongCollectionType", getTestFuncForVM(prog, nil, 1, 2)) 1532 1533 arr := makeArrayOfType(5, stackitem.BooleanT) 1534 t.Run("Array", func(t *testing.T) { 1535 t.Run("True", getTestFuncForVM(prog, true, stackitem.NewArray(arr), 4)) 1536 t.Run("too big", getTestFuncForVM(prog, nil, stackitem.NewArray(arr), maxu64Plus(4))) 1537 t.Run("False", getTestFuncForVM(prog, false, stackitem.NewArray(arr), 5)) 1538 }) 1539 t.Run("Struct", func(t *testing.T) { 1540 t.Run("True", getTestFuncForVM(prog, true, stackitem.NewStruct(arr), 4)) 1541 t.Run("too big", getTestFuncForVM(prog, nil, stackitem.NewStruct(arr), maxu64Plus(4))) 1542 t.Run("False", getTestFuncForVM(prog, false, stackitem.NewStruct(arr), 5)) 1543 }) 1544 1545 t.Run("Buffer", func(t *testing.T) { 1546 t.Run("True", getTestFuncForVM(prog, true, stackitem.NewBuffer([]byte{5, 5, 5}), 2)) 1547 t.Run("too big", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{5, 5, 5}), maxu64Plus(2))) 1548 t.Run("False", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{5, 5, 5}), 3)) 1549 t.Run("Negative", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{5, 5, 5}), -1)) 1550 }) 1551 1552 t.Run("ByteArray", func(t *testing.T) { 1553 t.Run("True", getTestFuncForVM(prog, true, stackitem.NewByteArray([]byte{5, 5, 5}), 2)) 1554 t.Run("too big", getTestFuncForVM(prog, nil, stackitem.NewByteArray([]byte{5, 5, 5}), maxu64Plus(2))) 1555 t.Run("False", getTestFuncForVM(prog, false, stackitem.NewByteArray([]byte{5, 5, 5}), 3)) 1556 t.Run("Negative", getTestFuncForVM(prog, nil, stackitem.NewByteArray([]byte{5, 5, 5}), -1)) 1557 }) 1558 } 1559 1560 func TestHASKEYMap(t *testing.T) { 1561 prog := makeProgram(opcode.HASKEY) 1562 m := stackitem.NewMap() 1563 m.Add(stackitem.Make(5), stackitem.Make(6)) 1564 t.Run("True", getTestFuncForVM(prog, true, m, 5)) 1565 t.Run("False", getTestFuncForVM(prog, false, m, 6)) 1566 } 1567 1568 func TestHASKEYBigKey(t *testing.T) { 1569 v := newTestVM() 1570 1571 buf := io.NewBufBinWriter() 1572 emit.Int(buf.BinWriter, 1024*1024) 1573 emit.Opcodes(buf.BinWriter, opcode.NEWBUFFER, opcode.NEWMAP) 1574 emit.Int(buf.BinWriter, 64) 1575 emit.Opcodes(buf.BinWriter, opcode.NEWBUFFER) 1576 emit.Opcodes(buf.BinWriter, opcode.INITSLOT, opcode.Opcode(0), opcode.Opcode(3)) 1577 1578 emit.Opcodes(buf.BinWriter, opcode.LDARG1, opcode.LDARG0, opcode.CONVERT, opcode.Opcode(stackitem.ByteArrayT)) 1579 emit.Opcodes(buf.BinWriter, opcode.LDARG0, opcode.SETITEM, opcode.LDARG1, opcode.LDARG2) 1580 emit.Opcodes(buf.BinWriter, opcode.CONVERT, opcode.Opcode(stackitem.ByteArrayT)) 1581 emit.Opcodes(buf.BinWriter, opcode.HASKEY) 1582 1583 emit.Opcodes(buf.BinWriter, opcode.JMP, opcode.Opcode(0xFB)) // -5 1584 v.Load(buf.Bytes()) 1585 checkVMFailed(t, v) 1586 } 1587 1588 func TestSIGN(t *testing.T) { 1589 prog := makeProgram(opcode.SIGN) 1590 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1591 t.Run("WrongType", getTestFuncForVM(prog, nil, []stackitem.Item{})) 1592 t.Run("Bool", getTestFuncForVM(prog, 0, false)) 1593 t.Run("PositiveInt", getTestFuncForVM(prog, 1, 2)) 1594 t.Run("NegativeInt", getTestFuncForVM(prog, -1, -1)) 1595 t.Run("Zero", getTestFuncForVM(prog, 0, 0)) 1596 t.Run("ByteArray", getTestFuncForVM(prog, 1, []byte{0, 1})) 1597 } 1598 1599 func TestSimpleCall(t *testing.T) { 1600 buf := io.NewBufBinWriter() 1601 w := buf.BinWriter 1602 emit.Opcodes(w, opcode.PUSH2) 1603 emit.Instruction(w, opcode.CALL, []byte{03}) 1604 emit.Opcodes(w, opcode.RET) 1605 emit.Opcodes(w, opcode.PUSH10) 1606 emit.Opcodes(w, opcode.ADD) 1607 emit.Opcodes(w, opcode.RET) 1608 1609 result := 12 1610 vm := load(buf.Bytes()) 1611 runVM(t, vm) 1612 assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64())) 1613 } 1614 1615 func TestNZ(t *testing.T) { 1616 prog := makeProgram(opcode.NZ) 1617 t.Run("True", getTestFuncForVM(prog, true, 1)) 1618 t.Run("False", getTestFuncForVM(prog, false, 0)) 1619 } 1620 1621 func TestPICK(t *testing.T) { 1622 prog := makeProgram(opcode.PICK) 1623 t.Run("NoItem", getTestFuncForVM(prog, nil, 1)) 1624 t.Run("Negative", getTestFuncForVM(prog, nil, -1)) 1625 t.Run("very big", getTestFuncForVM(prog, nil, 1, 2, 3, maxu64Plus(1))) 1626 } 1627 1628 func TestPICKgood(t *testing.T) { 1629 prog := makeProgram(opcode.PICK) 1630 result := 2 1631 vm := load(prog) 1632 vm.estack.PushVal(0) 1633 vm.estack.PushVal(1) 1634 vm.estack.PushVal(result) 1635 vm.estack.PushVal(3) 1636 vm.estack.PushVal(4) 1637 vm.estack.PushVal(5) 1638 vm.estack.PushVal(3) 1639 runVM(t, vm) 1640 assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64()) 1641 } 1642 1643 func TestPICKDup(t *testing.T) { 1644 prog := makeProgram(opcode.PUSHM1, opcode.PUSH0, 1645 opcode.PUSH1, 1646 opcode.PUSH2, 1647 opcode.PICK, 1648 opcode.ABS) 1649 vm := load(prog) 1650 runVM(t, vm) 1651 assert.Equal(t, 4, vm.estack.Len()) 1652 assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) 1653 assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) 1654 assert.Equal(t, int64(0), vm.estack.Pop().BigInt().Int64()) 1655 assert.Equal(t, int64(-1), vm.estack.Pop().BigInt().Int64()) 1656 } 1657 1658 func TestROTBad(t *testing.T) { 1659 prog := makeProgram(opcode.ROT) 1660 runWithArgs(t, prog, nil, 1, 2) 1661 } 1662 1663 func TestROTGood(t *testing.T) { 1664 prog := makeProgram(opcode.ROT) 1665 vm := load(prog) 1666 vm.estack.PushVal(1) 1667 vm.estack.PushVal(2) 1668 vm.estack.PushVal(3) 1669 runVM(t, vm) 1670 assert.Equal(t, 3, vm.estack.Len()) 1671 assert.Equal(t, stackitem.Make(1), vm.estack.Pop().value) 1672 assert.Equal(t, stackitem.Make(3), vm.estack.Pop().value) 1673 assert.Equal(t, stackitem.Make(2), vm.estack.Pop().value) 1674 } 1675 1676 func TestROLLBad1(t *testing.T) { 1677 prog := makeProgram(opcode.ROLL) 1678 runWithArgs(t, prog, nil, 1, -1) 1679 } 1680 1681 func TestROLLBad2(t *testing.T) { 1682 prog := makeProgram(opcode.ROLL) 1683 runWithArgs(t, prog, nil, 1, 2, 3, 3) 1684 } 1685 1686 func maxu64Plus(x int64) *big.Int { 1687 bi := new(big.Int).SetUint64(math.MaxUint64) 1688 bi.Add(bi, big.NewInt(2)) 1689 return bi 1690 } 1691 1692 func TestROLLBad3(t *testing.T) { 1693 prog := makeProgram(opcode.ROLL) 1694 runWithArgs(t, prog, nil, 1, 2, 3, maxu64Plus(2)) 1695 } 1696 1697 func TestROLLGood(t *testing.T) { 1698 prog := makeProgram(opcode.ROLL) 1699 vm := load(prog) 1700 vm.estack.PushVal(1) 1701 vm.estack.PushVal(2) 1702 vm.estack.PushVal(3) 1703 vm.estack.PushVal(4) 1704 vm.estack.PushVal(1) 1705 runVM(t, vm) 1706 assert.Equal(t, 4, vm.estack.Len()) 1707 assert.Equal(t, stackitem.Make(3), vm.estack.Pop().value) 1708 assert.Equal(t, stackitem.Make(4), vm.estack.Pop().value) 1709 assert.Equal(t, stackitem.Make(2), vm.estack.Pop().value) 1710 assert.Equal(t, stackitem.Make(1), vm.estack.Pop().value) 1711 } 1712 1713 func getCheckEStackFunc(items ...any) func(t *testing.T, v *VM) { 1714 return func(t *testing.T, v *VM) { 1715 require.Equal(t, len(items), v.estack.Len()) 1716 for i := 0; i < len(items); i++ { 1717 assert.Equal(t, stackitem.Make(items[i]), v.estack.Peek(i).Item()) 1718 } 1719 } 1720 } 1721 1722 func TestREVERSE3(t *testing.T) { 1723 prog := makeProgram(opcode.REVERSE3) 1724 t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2)) 1725 t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3), 1, 2, 3)) 1726 } 1727 1728 func TestREVERSE4(t *testing.T) { 1729 prog := makeProgram(opcode.REVERSE4) 1730 t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2, 3)) 1731 t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4), 1, 2, 3, 4)) 1732 } 1733 1734 func TestREVERSEN(t *testing.T) { 1735 prog := makeProgram(opcode.REVERSEN) 1736 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1737 t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2, 3)) 1738 t.Run("NegativeArgument", getTestFuncForVM(prog, nil, 1, 2, -1)) 1739 t.Run("Zero", getCustomTestFuncForVM(prog, getCheckEStackFunc(3, 2, 1), 1, 2, 3, 0)) 1740 t.Run("OneItem", getCustomTestFuncForVM(prog, getCheckEStackFunc(42), 42, 1)) 1741 t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4, 5), 1, 2, 3, 4, 5, 5)) 1742 t.Run("VeryBigNumber", getCustomTestFuncForVM(prog, nil, 1, 2, maxu64Plus(2))) 1743 } 1744 1745 func TestTUCKbadNoitems(t *testing.T) { 1746 prog := makeProgram(opcode.TUCK) 1747 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1748 t.Run("NoItem", getTestFuncForVM(prog, nil, 1)) 1749 } 1750 1751 func TestTUCKgood(t *testing.T) { 1752 prog := makeProgram(opcode.TUCK) 1753 vm := load(prog) 1754 vm.estack.PushVal(42) 1755 vm.estack.PushVal(34) 1756 runVM(t, vm) 1757 assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64()) 1758 assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64()) 1759 assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64()) 1760 } 1761 1762 func TestTUCKgood2(t *testing.T) { 1763 prog := makeProgram(opcode.TUCK) 1764 vm := load(prog) 1765 vm.estack.PushVal(11) 1766 vm.estack.PushVal(42) 1767 vm.estack.PushVal(34) 1768 runVM(t, vm) 1769 assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64()) 1770 assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64()) 1771 assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64()) 1772 assert.Equal(t, int64(11), vm.estack.Peek(3).BigInt().Int64()) 1773 } 1774 1775 func TestOVER(t *testing.T) { 1776 prog := makeProgram(opcode.OVER) 1777 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1778 t.Run("NoItem", getTestFuncForVM(prog, nil, 1)) 1779 } 1780 1781 func TestOVERgood(t *testing.T) { 1782 prog := makeProgram(opcode.OVER) 1783 vm := load(prog) 1784 vm.estack.PushVal(42) 1785 vm.estack.PushVal(34) 1786 runVM(t, vm) 1787 assert.Equal(t, int64(42), vm.estack.Peek(0).BigInt().Int64()) 1788 assert.Equal(t, int64(34), vm.estack.Peek(1).BigInt().Int64()) 1789 assert.Equal(t, int64(42), vm.estack.Peek(2).BigInt().Int64()) 1790 assert.Equal(t, 3, vm.estack.Len()) 1791 } 1792 1793 func TestOVERDup(t *testing.T) { 1794 prog := makeProgram(opcode.PUSHDATA1, 2, 1, 0, 1795 opcode.PUSH1, 1796 opcode.OVER, 1797 opcode.PUSH1, 1798 opcode.LEFT, 1799 opcode.PUSHDATA1, 1, 2, 1800 opcode.CAT) 1801 vm := load(prog) 1802 runVM(t, vm) 1803 assert.Equal(t, 3, vm.estack.Len()) 1804 assert.Equal(t, []byte{0x01, 0x02}, vm.estack.Pop().Bytes()) 1805 assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) 1806 assert.Equal(t, []byte{0x01, 0x00}, vm.estack.Pop().Bytes()) 1807 } 1808 1809 func TestNIPBadNoItem(t *testing.T) { 1810 prog := makeProgram(opcode.NIP) 1811 runWithArgs(t, prog, nil, 1) 1812 } 1813 1814 func TestNIPGood(t *testing.T) { 1815 prog := makeProgram(opcode.NIP) 1816 runWithArgs(t, prog, 2, 1, 2) 1817 } 1818 1819 func TestDROPBadNoItem(t *testing.T) { 1820 prog := makeProgram(opcode.DROP) 1821 runWithArgs(t, prog, nil) 1822 } 1823 1824 func TestDROPGood(t *testing.T) { 1825 prog := makeProgram(opcode.DROP) 1826 vm := load(prog) 1827 vm.estack.PushVal(1) 1828 runVM(t, vm) 1829 assert.Equal(t, 0, vm.estack.Len()) 1830 } 1831 1832 func TestXDROP(t *testing.T) { 1833 prog := makeProgram(opcode.XDROP) 1834 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1835 t.Run("NoN", getTestFuncForVM(prog, nil, 1, 2)) 1836 t.Run("very big argument", getTestFuncForVM(prog, nil, 1, 2, maxu64Plus(1))) 1837 t.Run("Negative", getTestFuncForVM(prog, nil, 1, -1)) 1838 } 1839 1840 func TestXDROPgood(t *testing.T) { 1841 prog := makeProgram(opcode.XDROP) 1842 vm := load(prog) 1843 vm.estack.PushVal(0) 1844 vm.estack.PushVal(1) 1845 vm.estack.PushVal(2) 1846 vm.estack.PushVal(2) 1847 runVM(t, vm) 1848 assert.Equal(t, 2, vm.estack.Len()) 1849 assert.Equal(t, int64(2), vm.estack.Peek(0).BigInt().Int64()) 1850 assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) 1851 } 1852 1853 func TestCLEAR(t *testing.T) { 1854 prog := makeProgram(opcode.CLEAR) 1855 v := load(prog) 1856 v.estack.PushVal(123) 1857 require.Equal(t, 1, v.estack.Len()) 1858 require.NoError(t, v.Run()) 1859 require.Equal(t, 0, v.estack.Len()) 1860 } 1861 1862 func TestINVERTbadNoitem(t *testing.T) { 1863 prog := makeProgram(opcode.INVERT) 1864 runWithArgs(t, prog, nil) 1865 } 1866 1867 func TestINVERTgood1(t *testing.T) { 1868 prog := makeProgram(opcode.INVERT) 1869 runWithArgs(t, prog, -1, 0) 1870 } 1871 1872 func TestINVERTgood2(t *testing.T) { 1873 prog := makeProgram(opcode.INVERT) 1874 vm := load(prog) 1875 vm.estack.PushVal(-1) 1876 runVM(t, vm) 1877 assert.Equal(t, int64(0), vm.estack.Peek(0).BigInt().Int64()) 1878 } 1879 1880 func TestINVERTgood3(t *testing.T) { 1881 prog := makeProgram(opcode.INVERT) 1882 vm := load(prog) 1883 vm.estack.PushVal(0x69) 1884 runVM(t, vm) 1885 assert.Equal(t, int64(-0x6A), vm.estack.Peek(0).BigInt().Int64()) 1886 } 1887 1888 func TestINVERTWithConversion1(t *testing.T) { 1889 prog := makeProgram(opcode.PUSHDATA2, 0, 0, opcode.INVERT) 1890 vm := load(prog) 1891 runVM(t, vm) 1892 assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64()) 1893 } 1894 1895 func TestINVERTWithConversion2(t *testing.T) { 1896 prog := makeProgram(opcode.PUSH0, opcode.PUSH1, opcode.NUMEQUAL, opcode.INVERT) 1897 vm := load(prog) 1898 runVM(t, vm) 1899 assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64()) 1900 } 1901 1902 func TestCAT(t *testing.T) { 1903 prog := makeProgram(opcode.CAT) 1904 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1905 t.Run("OneArgument", getTestFuncForVM(prog, nil, []byte("abc"))) 1906 t.Run("BigItem", func(t *testing.T) { 1907 arg := make([]byte, stackitem.MaxSize/2+1) 1908 runWithArgs(t, prog, nil, arg, arg) 1909 }) 1910 t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("abcdef")), []byte("abc"), []byte("def"))) 1911 t.Run("Int0ByteArray", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{}), 0, []byte{})) 1912 t.Run("ByteArrayInt1", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{1}), []byte{}, 1)) 1913 } 1914 1915 func TestSUBSTR(t *testing.T) { 1916 prog := makeProgram(opcode.SUBSTR) 1917 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1918 t.Run("OneArgument", getTestFuncForVM(prog, nil, 1)) 1919 t.Run("TwoArguments", getTestFuncForVM(prog, nil, 0, 2)) 1920 t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("bc")), []byte("abcdef"), 1, 2)) 1921 t.Run("BadOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), 7, 1)) 1922 t.Run("very big offset", getTestFuncForVM(prog, nil, []byte("abcdef"), maxu64Plus(1), 1)) 1923 t.Run("BigLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 1, 6)) 1924 t.Run("very big len", getTestFuncForVM(prog, nil, []byte("abcdef"), 1, maxu64Plus(1))) 1925 t.Run("NegativeOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), -1, 3)) 1926 t.Run("NegativeLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 3, -1)) 1927 } 1928 1929 func TestSUBSTRBad387(t *testing.T) { 1930 prog := makeProgram(opcode.SUBSTR) 1931 vm := load(prog) 1932 b := make([]byte, 6, 20) 1933 copy(b, "abcdef") 1934 vm.estack.PushVal(b) 1935 vm.estack.PushVal(1) 1936 vm.estack.PushVal(6) 1937 checkVMFailed(t, vm) 1938 } 1939 1940 func TestLEFT(t *testing.T) { 1941 prog := makeProgram(opcode.LEFT) 1942 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1943 t.Run("NoString", getTestFuncForVM(prog, nil, 2)) 1944 t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1)) 1945 t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("ab")), "abcdef", 2)) 1946 t.Run("BadBigLen", getTestFuncForVM(prog, nil, "abcdef", 8)) 1947 t.Run("bad, very big len", getTestFuncForVM(prog, nil, "abcdef", maxu64Plus(2))) 1948 } 1949 1950 func TestRIGHT(t *testing.T) { 1951 prog := makeProgram(opcode.RIGHT) 1952 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 1953 t.Run("NoString", getTestFuncForVM(prog, nil, 2)) 1954 t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1)) 1955 t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("ef")), "abcdef", 2)) 1956 t.Run("BadLen", getTestFuncForVM(prog, nil, "abcdef", 8)) 1957 t.Run("bad, very big len", getTestFuncForVM(prog, nil, "abcdef", maxu64Plus(2))) 1958 } 1959 1960 func TestPACK(t *testing.T) { 1961 for _, op := range []opcode.Opcode{opcode.PACK, opcode.PACKSTRUCT} { 1962 t.Run(op.String(), func(t *testing.T) { 1963 prog := makeProgram(op) 1964 t.Run("BadLen", getTestFuncForVM(prog, nil, 1)) 1965 t.Run("BigLen", getTestFuncForVM(prog, nil, 100500)) 1966 t.Run("Good0Len", getTestFuncForVM(prog, []stackitem.Item{}, 0)) 1967 }) 1968 } 1969 } 1970 1971 func TestPACKGood(t *testing.T) { 1972 for _, op := range []opcode.Opcode{opcode.PACK, opcode.PACKSTRUCT} { 1973 t.Run(op.String(), func(t *testing.T) { 1974 prog := makeProgram(op) 1975 elements := []int{55, 34, 42} 1976 vm := load(prog) 1977 // canary 1978 vm.estack.PushVal(1) 1979 for i := len(elements) - 1; i >= 0; i-- { 1980 vm.estack.PushVal(elements[i]) 1981 } 1982 vm.estack.PushVal(len(elements)) 1983 runVM(t, vm) 1984 assert.Equal(t, 2, vm.estack.Len()) 1985 a := vm.estack.Peek(0).Array() 1986 assert.Equal(t, len(elements), len(a)) 1987 for i := 0; i < len(elements); i++ { 1988 e := a[i].Value().(*big.Int) 1989 assert.Equal(t, int64(elements[i]), e.Int64()) 1990 } 1991 assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) 1992 }) 1993 } 1994 } 1995 1996 func TestPACK_UNPACK_MaxSize(t *testing.T) { 1997 prog := makeProgram(opcode.PACK, opcode.UNPACK) 1998 elements := make([]int, MaxStackSize-2) 1999 vm := load(prog) 2000 // canary 2001 vm.estack.PushVal(1) 2002 for i := len(elements) - 1; i >= 0; i-- { 2003 vm.estack.PushVal(elements[i]) 2004 } 2005 vm.estack.PushVal(len(elements)) 2006 runVM(t, vm) 2007 // check reference counter = 1+1+1024 2008 assert.Equal(t, 1+1+len(elements), int(vm.refs)) 2009 assert.Equal(t, 1+1+len(elements), vm.estack.Len()) // canary + length + elements 2010 assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).Value().(*big.Int).Int64()) 2011 for i := 0; i < len(elements); i++ { 2012 e, ok := vm.estack.Peek(i + 1).Value().(*big.Int) 2013 assert.True(t, ok) 2014 assert.Equal(t, int64(elements[i]), e.Int64()) 2015 } 2016 assert.Equal(t, int64(1), vm.estack.Peek(1+len(elements)).BigInt().Int64()) 2017 } 2018 2019 func TestPACK_UNPACK_PACK_MaxSize(t *testing.T) { 2020 prog := makeProgram(opcode.PACK, opcode.UNPACK, opcode.PACK) 2021 elements := make([]int, MaxStackSize-2) 2022 vm := load(prog) 2023 // canary 2024 vm.estack.PushVal(1) 2025 for i := len(elements) - 1; i >= 0; i-- { 2026 vm.estack.PushVal(elements[i]) 2027 } 2028 vm.estack.PushVal(len(elements)) 2029 runVM(t, vm) 2030 // check reference counter = 1+1+1024 2031 assert.Equal(t, 1+1+len(elements), int(vm.refs)) 2032 assert.Equal(t, 2, vm.estack.Len()) 2033 a := vm.estack.Peek(0).Array() 2034 assert.Equal(t, len(elements), len(a)) 2035 for i := 0; i < len(elements); i++ { 2036 e := a[i].Value().(*big.Int) 2037 assert.Equal(t, int64(elements[i]), e.Int64()) 2038 } 2039 assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) 2040 } 2041 2042 func TestPACKMAP_UNPACK_PACKMAP_MaxSize(t *testing.T) { 2043 prog := makeProgram(opcode.PACKMAP, opcode.UNPACK, opcode.PACKMAP) 2044 elements := make([]int, (MaxStackSize-2)/2) 2045 vm := load(prog) 2046 // canary 2047 vm.estack.PushVal(-1) 2048 for i := len(elements) - 1; i >= 0; i-- { 2049 elements[i] = i 2050 vm.estack.PushVal(i * 2) 2051 vm.estack.PushVal(i) 2052 } 2053 vm.estack.PushVal(len(elements)) 2054 runVM(t, vm) 2055 // check reference counter = 1+1+1024*2 2056 assert.Equal(t, 1+1+len(elements)*2, int(vm.refs)) 2057 assert.Equal(t, 2, vm.estack.Len()) 2058 m := vm.estack.Peek(0).value.(*stackitem.Map).Value().([]stackitem.MapElement) 2059 assert.Equal(t, len(elements), len(m)) 2060 for i := 0; i < len(elements); i++ { 2061 k := m[i].Key.Value().(*big.Int) 2062 v := m[i].Value.Value().(*big.Int) 2063 assert.Equal(t, int64(elements[i]), k.Int64()) 2064 assert.Equal(t, int64(elements[i])*2, v.Int64()) 2065 } 2066 assert.Equal(t, int64(-1), vm.estack.Peek(1).BigInt().Int64()) 2067 } 2068 2069 func TestPACKMAPBadKey(t *testing.T) { 2070 prog := makeProgram(opcode.PACKMAP) 2071 vm := load(prog) 2072 vm.estack.PushVal(1) 2073 vm.estack.PushItem(stackitem.NewBuffer([]byte{1})) 2074 vm.estack.PushVal(1) 2075 checkVMFailed(t, vm) 2076 } 2077 2078 func TestUNPACKBadNotArray(t *testing.T) { 2079 prog := makeProgram(opcode.UNPACK) 2080 runWithArgs(t, prog, nil, 1) 2081 } 2082 2083 func TestUNPACKGood(t *testing.T) { 2084 prog := makeProgram(opcode.UNPACK) 2085 elements := []int{55, 34, 42} 2086 vm := load(prog) 2087 // canary 2088 vm.estack.PushVal(1) 2089 vm.estack.PushVal(elements) 2090 runVM(t, vm) 2091 assert.Equal(t, 5, vm.estack.Len()) 2092 assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).BigInt().Int64()) 2093 for k, v := range elements { 2094 assert.Equal(t, int64(v), vm.estack.Peek(k+1).BigInt().Int64()) 2095 } 2096 assert.Equal(t, int64(1), vm.estack.Peek(len(elements)+1).BigInt().Int64()) 2097 } 2098 2099 func TestREVERSEITEMS(t *testing.T) { 2100 prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS) 2101 t.Run("InvalidItem", getTestFuncForVM(prog, nil, 1)) 2102 t.Run("Buffer", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{3, 2, 1}), stackitem.NewBuffer([]byte{1, 2, 3}))) 2103 } 2104 2105 func testREVERSEITEMSIssue437(t *testing.T, i1 opcode.Opcode, t2 stackitem.Type, reversed bool) { 2106 prog := makeProgram( 2107 opcode.PUSH0, i1, 2108 opcode.DUP, opcode.PUSH1, opcode.APPEND, 2109 opcode.DUP, opcode.PUSH2, opcode.APPEND, 2110 opcode.DUP, opcode.CONVERT, opcode.Opcode(t2), opcode.REVERSEITEMS) 2111 2112 arr := make([]stackitem.Item, 2) 2113 if reversed { 2114 arr[0] = stackitem.Make(2) 2115 arr[1] = stackitem.Make(1) 2116 } else { 2117 arr[0] = stackitem.Make(1) 2118 arr[1] = stackitem.Make(2) 2119 } 2120 2121 if i1 == opcode.NEWARRAY { 2122 runWithArgs(t, prog, stackitem.NewArray(arr)) 2123 } else { 2124 runWithArgs(t, prog, stackitem.NewStruct(arr)) 2125 } 2126 } 2127 2128 func TestREVERSEITEMSIssue437(t *testing.T) { 2129 t.Run("Array+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, stackitem.ArrayT, true) }) 2130 t.Run("Struct+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, stackitem.StructT, true) }) 2131 t.Run("Array+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, stackitem.StructT, false) }) 2132 t.Run("Struct+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, stackitem.ArrayT, false) }) 2133 } 2134 2135 func TestREVERSEITEMSGoodOneElem(t *testing.T) { 2136 prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS) 2137 elements := []int{22} 2138 vm := load(prog) 2139 vm.estack.PushVal(1) 2140 vm.estack.PushVal(elements) 2141 runVM(t, vm) 2142 assert.Equal(t, 2, vm.estack.Len()) 2143 a := vm.estack.Peek(0).Array() 2144 assert.Equal(t, len(elements), len(a)) 2145 e := a[0].Value().(*big.Int) 2146 assert.Equal(t, int64(elements[0]), e.Int64()) 2147 } 2148 2149 func TestREVERSEITEMSGoodStruct(t *testing.T) { 2150 eodd := []int{22, 34, 42, 55, 81} 2151 even := []int{22, 34, 42, 55, 81, 99} 2152 eall := [][]int{eodd, even} 2153 2154 for _, elements := range eall { 2155 prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS) 2156 vm := load(prog) 2157 vm.estack.PushVal(1) 2158 2159 arr := make([]stackitem.Item, len(elements)) 2160 for i := range elements { 2161 arr[i] = stackitem.Make(elements[i]) 2162 } 2163 vm.estack.Push(Element{value: stackitem.NewStruct(arr)}) 2164 2165 runVM(t, vm) 2166 assert.Equal(t, 2, vm.estack.Len()) 2167 a := vm.estack.Peek(0).Array() 2168 assert.Equal(t, len(elements), len(a)) 2169 for k, v := range elements { 2170 e := a[len(a)-1-k].Value().(*big.Int) 2171 assert.Equal(t, int64(v), e.Int64()) 2172 } 2173 assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) 2174 } 2175 } 2176 2177 func TestREVERSEITEMSGood(t *testing.T) { 2178 eodd := []int{22, 34, 42, 55, 81} 2179 even := []int{22, 34, 42, 55, 81, 99} 2180 eall := [][]int{eodd, even} 2181 2182 for _, elements := range eall { 2183 prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS) 2184 vm := load(prog) 2185 vm.estack.PushVal(1) 2186 vm.estack.PushVal(elements) 2187 runVM(t, vm) 2188 assert.Equal(t, 2, vm.estack.Len()) 2189 a := vm.estack.Peek(0).Array() 2190 assert.Equal(t, len(elements), len(a)) 2191 for k, v := range elements { 2192 e := a[len(a)-1-k].Value().(*big.Int) 2193 assert.Equal(t, int64(v), e.Int64()) 2194 } 2195 assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) 2196 } 2197 } 2198 2199 func TestREMOVE(t *testing.T) { 2200 prog := makeProgram(opcode.REMOVE) 2201 t.Run("NoArgument", getTestFuncForVM(prog, nil)) 2202 t.Run("OneArgument", getTestFuncForVM(prog, nil, 1)) 2203 t.Run("NotArray", getTestFuncForVM(prog, nil, 1, 1)) 2204 t.Run("BadIndex", getTestFuncForVM(prog, nil, []int{22, 34, 42, 55, 81}, 10)) 2205 t.Run("very big index", getTestFuncForVM(prog, nil, []int{22, 34, 42, 55, 81}, maxu64Plus(1))) 2206 } 2207 2208 func TestREMOVEGood(t *testing.T) { 2209 prog := makeProgram(opcode.DUP, opcode.PUSH2, opcode.REMOVE) 2210 elements := []int{22, 34, 42, 55, 81} 2211 reselements := []int{22, 34, 55, 81} 2212 vm := load(prog) 2213 vm.estack.PushVal(1) 2214 vm.estack.PushVal(elements) 2215 runVM(t, vm) 2216 assert.Equal(t, 2, vm.estack.Len()) 2217 assert.Equal(t, stackitem.Make(reselements), vm.estack.Pop().value) 2218 assert.Equal(t, stackitem.Make(1), vm.estack.Pop().value) 2219 } 2220 2221 func TestREMOVEMap(t *testing.T) { 2222 prog := makeProgram(opcode.REMOVE, opcode.PUSH5, opcode.HASKEY) 2223 vm := load(prog) 2224 2225 m := stackitem.NewMap() 2226 m.Add(stackitem.Make(5), stackitem.Make(3)) 2227 m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make([]byte{2, 3})) 2228 vm.estack.Push(Element{value: m}) 2229 vm.estack.Push(Element{value: m}) 2230 vm.estack.PushVal(stackitem.Make(5)) 2231 2232 runVM(t, vm) 2233 assert.Equal(t, 1, vm.estack.Len()) 2234 assert.Equal(t, stackitem.Make(false), vm.estack.Pop().value) 2235 } 2236 2237 func testCLEARITEMS(t *testing.T, item stackitem.Item) { 2238 prog := makeProgram(opcode.DUP, opcode.DUP, opcode.CLEARITEMS, opcode.SIZE) 2239 v := load(prog) 2240 v.estack.PushVal(item) 2241 runVM(t, v) 2242 require.Equal(t, 2, v.estack.Len()) 2243 require.EqualValues(t, 2, int(v.refs)) // empty collection + it's size 2244 require.EqualValues(t, 0, v.estack.Pop().BigInt().Int64()) 2245 } 2246 2247 func TestCLEARITEMS(t *testing.T) { 2248 arr := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewByteArray([]byte{1})} 2249 m := stackitem.NewMap() 2250 m.Add(stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewByteArray([]byte{})) 2251 m.Add(stackitem.NewByteArray([]byte{42}), stackitem.NewBigInteger(big.NewInt(2))) 2252 2253 testCases := map[string]stackitem.Item{ 2254 "empty Array": stackitem.NewArray([]stackitem.Item{}), 2255 "filled Array": stackitem.NewArray(arr), 2256 "empty Struct": stackitem.NewStruct([]stackitem.Item{}), 2257 "filled Struct": stackitem.NewStruct(arr), 2258 "empty Map": stackitem.NewMap(), 2259 "filled Map": m, 2260 } 2261 2262 for name, item := range testCases { 2263 t.Run(name, func(t *testing.T) { testCLEARITEMS(t, item) }) 2264 } 2265 2266 t.Run("Integer", func(t *testing.T) { 2267 prog := makeProgram(opcode.CLEARITEMS) 2268 runWithArgs(t, prog, nil, 1) 2269 }) 2270 } 2271 2272 func TestPOPITEM(t *testing.T) { 2273 testPOPITEM := func(t *testing.T, item, elem, arr any) { 2274 prog := makeProgram(opcode.DUP, opcode.POPITEM) 2275 v := load(prog) 2276 v.estack.PushVal(item) 2277 2278 runVM(t, v) 2279 require.EqualValues(t, stackitem.Make(elem), v.estack.Pop().Item()) 2280 require.EqualValues(t, stackitem.Make(arr), v.estack.Pop().Item()) 2281 } 2282 2283 elems := []stackitem.Item{stackitem.Make(11), stackitem.Make(31)} 2284 t.Run("Array", func(t *testing.T) { 2285 testPOPITEM(t, stackitem.NewArray(elems), 31, elems[:1]) 2286 }) 2287 t.Run("Struct", func(t *testing.T) { 2288 testPOPITEM(t, stackitem.NewStruct(elems), 31, elems[:1]) 2289 }) 2290 t.Run("0-length array", func(t *testing.T) { 2291 prog := makeProgram(opcode.NEWARRAY0, opcode.POPITEM) 2292 v := load(prog) 2293 checkVMFailed(t, v) 2294 }) 2295 t.Run("primitive type", func(t *testing.T) { 2296 prog := makeProgram(opcode.PUSH4, opcode.POPITEM) 2297 v := load(prog) 2298 checkVMFailed(t, v) 2299 }) 2300 } 2301 2302 func TestSWAPGood(t *testing.T) { 2303 prog := makeProgram(opcode.SWAP) 2304 vm := load(prog) 2305 vm.estack.PushVal(2) 2306 vm.estack.PushVal(4) 2307 runVM(t, vm) 2308 assert.Equal(t, 2, vm.estack.Len()) 2309 assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64()) 2310 assert.Equal(t, int64(4), vm.estack.Pop().BigInt().Int64()) 2311 } 2312 2313 func TestSWAP(t *testing.T) { 2314 prog := makeProgram(opcode.SWAP) 2315 t.Run("EmptyStack", getTestFuncForVM(prog, nil)) 2316 t.Run("SmallStack", getTestFuncForVM(prog, nil, 4)) 2317 } 2318 2319 func TestDupInt(t *testing.T) { 2320 prog := makeProgram(opcode.DUP, opcode.ABS) 2321 vm := load(prog) 2322 vm.estack.PushVal(-1) 2323 runVM(t, vm) 2324 assert.Equal(t, 2, vm.estack.Len()) 2325 assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) 2326 assert.Equal(t, int64(-1), vm.estack.Pop().BigInt().Int64()) 2327 } 2328 2329 func TestNegateCopy(t *testing.T) { 2330 prog := makeProgram(opcode.NEGATE) 2331 v := load(prog) 2332 bi := stackitem.Make(-1) 2333 v.estack.PushVal(bi) 2334 v.estack.PushVal(bi) 2335 runVM(t, v) 2336 assert.Equal(t, 2, v.estack.Len()) 2337 assert.Equal(t, int64(1), v.estack.Pop().BigInt().Int64()) 2338 assert.Equal(t, int64(-1), v.estack.Pop().BigInt().Int64()) 2339 } 2340 2341 func TestDupByteArray(t *testing.T) { 2342 prog := makeProgram(opcode.PUSHDATA1, 2, 1, 0, 2343 opcode.DUP, 2344 opcode.PUSH1, 2345 opcode.LEFT, 2346 opcode.PUSHDATA1, 1, 2, 2347 opcode.CAT) 2348 vm := load(prog) 2349 runVM(t, vm) 2350 assert.Equal(t, 2, vm.estack.Len()) 2351 assert.Equal(t, []byte{0x01, 0x02}, vm.estack.Pop().Bytes()) 2352 assert.Equal(t, []byte{0x01, 0x00}, vm.estack.Pop().Bytes()) 2353 } 2354 2355 func TestDupBool(t *testing.T) { 2356 prog := makeProgram(opcode.PUSH0, opcode.NOT, 2357 opcode.DUP, 2358 opcode.PUSH1, opcode.NOT, 2359 opcode.BOOLAND) 2360 vm := load(prog) 2361 runVM(t, vm) 2362 assert.Equal(t, 2, vm.estack.Len()) 2363 assert.Equal(t, false, vm.estack.Pop().Bool()) 2364 assert.Equal(t, true, vm.estack.Pop().Bool()) 2365 } 2366 2367 var opcodesTestCases = map[opcode.Opcode][]struct { 2368 name string 2369 args []any 2370 expected any 2371 actual func(vm *VM) any 2372 }{ 2373 opcode.AND: { 2374 { 2375 name: "1_1", 2376 args: []any{1, 1}, 2377 expected: int64(1), 2378 actual: func(vm *VM) any { 2379 return vm.estack.Pop().BigInt().Int64() 2380 }, 2381 }, 2382 { 2383 name: "1_0", 2384 args: []any{1, 0}, 2385 expected: int64(0), 2386 actual: func(vm *VM) any { 2387 return vm.estack.Pop().BigInt().Int64() 2388 }, 2389 }, 2390 { 2391 name: "0_1", 2392 args: []any{0, 1}, 2393 expected: int64(0), 2394 actual: func(vm *VM) any { 2395 return vm.estack.Pop().BigInt().Int64() 2396 }, 2397 }, 2398 { 2399 name: "0_0", 2400 args: []any{0, 0}, 2401 expected: int64(0), 2402 actual: func(vm *VM) any { 2403 return vm.estack.Pop().BigInt().Int64() 2404 }, 2405 }, 2406 { 2407 name: "random_values", 2408 args: []any{ 2409 []byte{1, 0, 1, 0, 1, 0, 1, 1}, 2410 []byte{1, 1, 0, 0, 0, 0, 0, 1}, 2411 }, 2412 expected: []byte{1, 0, 0, 0, 0, 0, 0, 1}, 2413 actual: func(vm *VM) any { 2414 return vm.estack.Pop().Bytes() 2415 }, 2416 }, 2417 }, 2418 opcode.OR: { 2419 { 2420 name: "1_1", 2421 args: []any{1, 1}, 2422 expected: int64(1), 2423 actual: func(vm *VM) any { 2424 return vm.estack.Pop().BigInt().Int64() 2425 }, 2426 }, 2427 { 2428 name: "0_0", 2429 args: []any{0, 0}, 2430 expected: int64(0), 2431 actual: func(vm *VM) any { 2432 return vm.estack.Pop().BigInt().Int64() 2433 }, 2434 }, 2435 { 2436 name: "0_1", 2437 args: []any{0, 1}, 2438 expected: int64(1), 2439 actual: func(vm *VM) any { 2440 return vm.estack.Pop().BigInt().Int64() 2441 }, 2442 }, 2443 { 2444 name: "1_0", 2445 args: []any{1, 0}, 2446 expected: int64(1), 2447 actual: func(vm *VM) any { 2448 return vm.estack.Pop().BigInt().Int64() 2449 }, 2450 }, 2451 { 2452 name: "random_values", 2453 args: []any{ 2454 []byte{1, 0, 1, 0, 1, 0, 1, 1}, 2455 []byte{1, 1, 0, 0, 0, 0, 0, 1}, 2456 }, 2457 expected: []byte{1, 1, 1, 0, 1, 0, 1, 1}, 2458 actual: func(vm *VM) any { 2459 return vm.estack.Pop().Bytes() 2460 }, 2461 }, 2462 }, 2463 opcode.XOR: { 2464 { 2465 name: "1_1", 2466 args: []any{1, 1}, 2467 expected: int64(0), 2468 actual: func(vm *VM) any { 2469 return vm.estack.Pop().BigInt().Int64() 2470 }, 2471 }, 2472 { 2473 name: "0_0", 2474 args: []any{0, 0}, 2475 expected: int64(0), 2476 actual: func(vm *VM) any { 2477 return vm.estack.Pop().BigInt().Int64() 2478 }, 2479 }, 2480 { 2481 name: "0_1", 2482 args: []any{0, 1}, 2483 expected: int64(1), 2484 actual: func(vm *VM) any { 2485 return vm.estack.Pop().BigInt().Int64() 2486 }, 2487 }, 2488 { 2489 name: "1_0", 2490 args: []any{1, 0}, 2491 expected: int64(1), 2492 actual: func(vm *VM) any { 2493 return vm.estack.Pop().BigInt().Int64() 2494 }, 2495 }, 2496 { 2497 name: "random_values", 2498 args: []any{ 2499 []byte{1, 0, 1, 0, 1, 0, 1, 1}, 2500 []byte{1, 1, 0, 0, 0, 0, 0, 1}, 2501 }, 2502 expected: []byte{0, 1, 1, 0, 1, 0, 1}, 2503 actual: func(vm *VM) any { 2504 return vm.estack.Pop().Bytes() 2505 }, 2506 }, 2507 }, 2508 opcode.BOOLOR: { 2509 { 2510 name: "1_1", 2511 args: []any{true, true}, 2512 expected: true, 2513 actual: func(vm *VM) any { 2514 return vm.estack.Pop().Bool() 2515 }, 2516 }, 2517 { 2518 name: "0_0", 2519 args: []any{false, false}, 2520 expected: false, 2521 actual: func(vm *VM) any { 2522 return vm.estack.Pop().Bool() 2523 }, 2524 }, 2525 { 2526 name: "0_1", 2527 args: []any{false, true}, 2528 expected: true, 2529 actual: func(vm *VM) any { 2530 return vm.estack.Pop().Bool() 2531 }, 2532 }, 2533 { 2534 name: "1_0", 2535 args: []any{true, false}, 2536 expected: true, 2537 actual: func(vm *VM) any { 2538 return vm.estack.Pop().Bool() 2539 }, 2540 }, 2541 }, 2542 opcode.MIN: { 2543 { 2544 name: "3_5", 2545 args: []any{3, 5}, 2546 expected: int64(3), 2547 actual: func(vm *VM) any { 2548 return vm.estack.Pop().BigInt().Int64() 2549 }, 2550 }, 2551 { 2552 name: "5_3", 2553 args: []any{5, 3}, 2554 expected: int64(3), 2555 actual: func(vm *VM) any { 2556 return vm.estack.Pop().BigInt().Int64() 2557 }, 2558 }, 2559 { 2560 name: "3_3", 2561 args: []any{3, 3}, 2562 expected: int64(3), 2563 actual: func(vm *VM) any { 2564 return vm.estack.Pop().BigInt().Int64() 2565 }, 2566 }, 2567 }, 2568 opcode.MAX: { 2569 { 2570 name: "3_5", 2571 args: []any{3, 5}, 2572 expected: int64(5), 2573 actual: func(vm *VM) any { 2574 return vm.estack.Pop().BigInt().Int64() 2575 }, 2576 }, 2577 { 2578 name: "5_3", 2579 args: []any{5, 3}, 2580 expected: int64(5), 2581 actual: func(vm *VM) any { 2582 return vm.estack.Pop().BigInt().Int64() 2583 }, 2584 }, 2585 { 2586 name: "3_3", 2587 args: []any{3, 3}, 2588 expected: int64(3), 2589 actual: func(vm *VM) any { 2590 return vm.estack.Pop().BigInt().Int64() 2591 }, 2592 }, 2593 }, 2594 opcode.WITHIN: { 2595 { 2596 name: "within", 2597 args: []any{4, 3, 5}, 2598 expected: true, 2599 actual: func(vm *VM) any { 2600 return vm.estack.Pop().Bool() 2601 }, 2602 }, 2603 { 2604 name: "less", 2605 args: []any{2, 3, 5}, 2606 expected: false, 2607 actual: func(vm *VM) any { 2608 return vm.estack.Pop().Bool() 2609 }, 2610 }, 2611 { 2612 name: "more", 2613 args: []any{6, 3, 5}, 2614 expected: false, 2615 actual: func(vm *VM) any { 2616 return vm.estack.Pop().Bool() 2617 }, 2618 }, 2619 }, 2620 opcode.NEGATE: { 2621 { 2622 name: "3", 2623 args: []any{3}, 2624 expected: int64(-3), 2625 actual: func(vm *VM) any { 2626 return vm.estack.Pop().BigInt().Int64() 2627 }, 2628 }, 2629 { 2630 name: "-3", 2631 args: []any{-3}, 2632 expected: int64(3), 2633 actual: func(vm *VM) any { 2634 return vm.estack.Pop().BigInt().Int64() 2635 }, 2636 }, 2637 { 2638 name: "0", 2639 args: []any{0}, 2640 expected: int64(0), 2641 actual: func(vm *VM) any { 2642 return vm.estack.Pop().BigInt().Int64() 2643 }, 2644 }, 2645 }, 2646 } 2647 2648 func TestBitAndNumericOpcodes(t *testing.T) { 2649 for code, opcodeTestCases := range opcodesTestCases { 2650 t.Run(code.String(), func(t *testing.T) { 2651 for _, testCase := range opcodeTestCases { 2652 prog := makeProgram(code) 2653 vm := load(prog) 2654 t.Run(testCase.name, func(t *testing.T) { 2655 for _, arg := range testCase.args { 2656 vm.estack.PushVal(arg) 2657 } 2658 runVM(t, vm) 2659 assert.Equal(t, testCase.expected, testCase.actual(vm)) 2660 }) 2661 } 2662 }) 2663 } 2664 } 2665 2666 func TestSLOTOpcodes(t *testing.T) { 2667 t.Run("Fail", func(t *testing.T) { 2668 t.Run("EmptyStatic", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 0), nil)) 2669 t.Run("EmptyLocal", getTestFuncForVM(makeProgram(opcode.INITSLOT, 0, 0), nil)) 2670 t.Run("NotEnoughArguments", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 0, 2), nil, 1)) 2671 t.Run("DoubleStatic", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 1, opcode.INITSSLOT, 1), nil)) 2672 t.Run("DoubleLocal", getTestFuncForVM(makeProgram(opcode.INITSLOT, 1, 0, opcode.INITSLOT, 1, 0), nil)) 2673 t.Run("DoubleArgument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 0, 1, opcode.INITSLOT, 0, 1), nil, 1, 2)) 2674 t.Run("LoadBigStatic", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 2, opcode.LDSFLD2), nil)) 2675 t.Run("LoadBigLocal", getTestFuncForVM(makeProgram(opcode.INITSLOT, 2, 2, opcode.LDLOC2), nil, 1, 2)) 2676 t.Run("LoadBigArgument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 2, 2, opcode.LDARG2), nil, 1, 2)) 2677 t.Run("StoreBigStatic", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 2, opcode.STSFLD2), nil, 0)) 2678 t.Run("StoreBigLocal", getTestFuncForVM(makeProgram(opcode.INITSLOT, 2, 2, opcode.STLOC2), nil, 0, 1, 2)) 2679 t.Run("StoreBigArgument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 2, 2, opcode.STARG2), nil, 0, 1, 2)) 2680 }) 2681 2682 t.Run("Default", func(t *testing.T) { 2683 t.Run("DefaultStatic", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 2, opcode.LDSFLD1), stackitem.Null{})) 2684 t.Run("DefaultLocal", getTestFuncForVM(makeProgram(opcode.INITSLOT, 2, 0, opcode.LDLOC1), stackitem.Null{})) 2685 t.Run("DefaultArgument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 0, 2, opcode.LDARG1), 2, 2, 1)) 2686 }) 2687 2688 t.Run("Set/Get", func(t *testing.T) { 2689 t.Run("FailCrossLoads", func(t *testing.T) { 2690 t.Run("Static/Local", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 2, opcode.LDLOC1), nil)) 2691 t.Run("Static/Argument", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 2, opcode.LDARG1), nil)) 2692 t.Run("Local/Argument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 0, 2, opcode.LDLOC1), nil)) 2693 t.Run("Argument/Local", getTestFuncForVM(makeProgram(opcode.INITSLOT, 2, 0, opcode.LDARG1), nil)) 2694 }) 2695 2696 t.Run("Static", getTestFuncForVM(makeProgram(opcode.INITSSLOT, 8, opcode.STSFLD, 7, opcode.LDSFLD, 7), 42, 42)) 2697 t.Run("Local", getTestFuncForVM(makeProgram(opcode.INITSLOT, 8, 0, opcode.STLOC, 7, opcode.LDLOC, 7), 42, 42)) 2698 t.Run("Argument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 0, 2, opcode.STARG, 1, opcode.LDARG, 1), 42, 42, 1, 2)) 2699 }) 2700 2701 t.Run("InitStaticSlotInMethod", func(t *testing.T) { 2702 prog := makeProgram( 2703 opcode.CALL, 4, opcode.LDSFLD0, opcode.RET, 2704 opcode.INITSSLOT, 1, opcode.PUSH12, opcode.STSFLD0, opcode.RET, 2705 ) 2706 runWithArgs(t, prog, 12) 2707 }) 2708 } 2709 2710 func TestNestedStructClone(t *testing.T) { 2711 progs := []string{ 2712 // VALUES for deeply nested structs, see neo-project/neo#2534. 2713 "5601c501fe0360589d604a12c0db415824f7cd45", 2714 // APPEND of deeply nested struct to empty array. 2715 "5601c2c501fe0360589d604a12c0db415824f7cf45", 2716 // VALUES for map with deeply nested struct. 2717 "5601c84a11c501fe0060589d604a12c0db415824f7d0cd45", 2718 // VALUES for a lot of not-so-deep nested structs. 2719 "5601c5000a60589d604a12c0db415824f701fe03504a519d4a102afa01ff03c0cd45", 2720 } 2721 for _, h := range progs { 2722 prog, err := hex.DecodeString(h) 2723 require.NoError(t, err) 2724 vm := load(prog) 2725 checkVMFailed(t, vm) 2726 } 2727 } 2728 2729 func TestNestedStructEquals(t *testing.T) { 2730 h := "560112c501fe0160589d604a12c0db415824f7509d4a102aec4597" // See neo-project/neo-vm#426. 2731 prog, err := hex.DecodeString(h) 2732 require.NoError(t, err) 2733 vm := load(prog) 2734 checkVMFailed(t, vm) 2735 } 2736 2737 func TestRemoveReferrer(t *testing.T) { 2738 h := "560110c34a10c36058cf4540" // #2501 2739 prog, err := hex.DecodeString(h) 2740 require.NoError(t, err) 2741 vm := load(prog) 2742 require.NoError(t, vm.StepInto()) // INITSSLOT 2743 assert.Equal(t, 1, int(vm.refs)) 2744 require.NoError(t, vm.StepInto()) // PUSH0 2745 assert.Equal(t, 2, int(vm.refs)) 2746 require.NoError(t, vm.StepInto()) // NEWARRAY 2747 assert.Equal(t, 2, int(vm.refs)) 2748 require.NoError(t, vm.StepInto()) // DUP 2749 assert.Equal(t, 3, int(vm.refs)) 2750 require.NoError(t, vm.StepInto()) // PUSH0 2751 assert.Equal(t, 4, int(vm.refs)) 2752 require.NoError(t, vm.StepInto()) // NEWARRAY 2753 assert.Equal(t, 4, int(vm.refs)) 2754 require.NoError(t, vm.StepInto()) // STSFLD0 2755 assert.Equal(t, 3, int(vm.refs)) 2756 require.NoError(t, vm.StepInto()) // LDSFLD0 2757 assert.Equal(t, 4, int(vm.refs)) 2758 require.NoError(t, vm.StepInto()) // APPEND 2759 assert.Equal(t, 3, int(vm.refs)) 2760 require.NoError(t, vm.StepInto()) // DROP 2761 assert.Equal(t, 1, int(vm.refs)) 2762 require.NoError(t, vm.StepInto()) // RET 2763 assert.Equal(t, 0, int(vm.refs)) 2764 } 2765 2766 func TestUninitializedSyscallHandler(t *testing.T) { 2767 v := newTestVM() 2768 v.Reset(trigger.Application) // Reset SyscallHandler. 2769 id := make([]byte, 4) 2770 binary.LittleEndian.PutUint32(id, interopnames.ToID([]byte(interopnames.SystemRuntimeGasLeft))) 2771 script := append([]byte{byte(opcode.SYSCALL)}, id...) 2772 v.LoadScript(script) 2773 err := v.Run() 2774 require.Error(t, err) 2775 require.True(t, strings.Contains(err.Error(), "SyscallHandler is not initialized"), err.Error()) 2776 assert.Equal(t, true, v.HasFailed()) 2777 } 2778 2779 func makeProgram(opcodes ...opcode.Opcode) []byte { 2780 prog := make([]byte, len(opcodes)+1) // RET 2781 for i := 0; i < len(opcodes); i++ { 2782 prog[i] = byte(opcodes[i]) 2783 } 2784 prog[len(prog)-1] = byte(opcode.RET) 2785 return prog 2786 } 2787 2788 func load(prog []byte) *VM { 2789 vm := newTestVM() 2790 if len(prog) != 0 { 2791 vm.LoadScript(prog) 2792 } 2793 return vm 2794 } 2795 2796 func randomBytes(n int) []byte { 2797 const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 2798 b := make([]byte, n) 2799 for i := range b { 2800 b[i] = charset[rand.Intn(len(charset))] 2801 } 2802 return b 2803 } 2804 2805 func newTestVM() *VM { 2806 v := New() 2807 v.GasLimit = -1 2808 return v 2809 }