github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/vm/emit/emit_test.go (about) 1 package emit 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "math" 7 "math/big" 8 "strings" 9 "testing" 10 11 "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" 12 "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" 13 "github.com/nspcc-dev/neo-go/pkg/io" 14 "github.com/nspcc-dev/neo-go/pkg/util" 15 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 16 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestEmitInt(t *testing.T) { 22 t.Run("minis one", func(t *testing.T) { 23 buf := io.NewBufBinWriter() 24 Int(buf.BinWriter, -1) 25 result := buf.Bytes() 26 assert.Len(t, result, 1) 27 assert.EqualValues(t, opcode.PUSHM1, result[0]) 28 }) 29 30 t.Run("zero", func(t *testing.T) { 31 buf := io.NewBufBinWriter() 32 Int(buf.BinWriter, 0) 33 result := buf.Bytes() 34 assert.Len(t, result, 1) 35 assert.EqualValues(t, opcode.PUSH0, result[0]) 36 }) 37 38 t.Run("1-byte int", func(t *testing.T) { 39 buf := io.NewBufBinWriter() 40 Int(buf.BinWriter, 10) 41 result := buf.Bytes() 42 assert.EqualValues(t, opcode.PUSH10, result[0]) 43 }) 44 45 t.Run("big 1-byte int", func(t *testing.T) { 46 buf := io.NewBufBinWriter() 47 Int(buf.BinWriter, 42) 48 result := buf.Bytes() 49 assert.EqualValues(t, opcode.PUSHINT8, result[0]) 50 assert.EqualValues(t, 42, result[1]) 51 }) 52 53 t.Run("2-byte int", func(t *testing.T) { 54 buf := io.NewBufBinWriter() 55 Int(buf.BinWriter, 300) 56 result := buf.Bytes() 57 assert.Equal(t, 3, len(result)) 58 assert.EqualValues(t, opcode.PUSHINT16, result[0]) 59 assert.EqualValues(t, 300, bigint.FromBytes(result[1:]).Int64()) 60 }) 61 62 t.Run("3-byte int", func(t *testing.T) { 63 buf := io.NewBufBinWriter() 64 Int(buf.BinWriter, 1<<20) 65 result := buf.Bytes() 66 assert.Equal(t, 5, len(result)) 67 assert.EqualValues(t, opcode.PUSHINT32, result[0]) 68 assert.EqualValues(t, 1<<20, bigint.FromBytes(result[1:]).Int64()) 69 }) 70 71 t.Run("4-byte int", func(t *testing.T) { 72 buf := io.NewBufBinWriter() 73 Int(buf.BinWriter, 1<<28) 74 result := buf.Bytes() 75 assert.Equal(t, 5, len(result)) 76 assert.EqualValues(t, opcode.PUSHINT32, result[0]) 77 assert.EqualValues(t, 1<<28, bigint.FromBytes(result[1:]).Int64()) 78 }) 79 80 t.Run("negative 3-byte int with padding", func(t *testing.T) { 81 const num = -(1 << 23) 82 buf := io.NewBufBinWriter() 83 Int(buf.BinWriter, num) 84 result := buf.Bytes() 85 assert.Equal(t, 5, len(result)) 86 assert.EqualValues(t, opcode.PUSHINT32, result[0]) 87 assert.EqualValues(t, num, bigint.FromBytes(result[1:]).Int64()) 88 }) 89 } 90 91 func TestEmitBigInt(t *testing.T) { 92 t.Run("biggest positive number", func(t *testing.T) { 93 buf := io.NewBufBinWriter() 94 bi := big.NewInt(1) 95 bi.Lsh(bi, 255) 96 bi.Sub(bi, big.NewInt(1)) 97 98 // sanity check 99 require.NotPanics(t, func() { stackitem.NewBigInteger(bi) }) 100 101 BigInt(buf.BinWriter, bi) 102 require.NoError(t, buf.Err) 103 104 expected := make([]byte, 33) 105 expected[0] = byte(opcode.PUSHINT256) 106 for i := 1; i < 32; i++ { 107 expected[i] = 0xFF 108 } 109 expected[32] = 0x7F 110 require.Equal(t, expected, buf.Bytes()) 111 }) 112 t.Run("smallest negative number", func(t *testing.T) { 113 buf := io.NewBufBinWriter() 114 bi := big.NewInt(-1) 115 bi.Lsh(bi, 255) 116 117 // sanity check 118 require.NotPanics(t, func() { stackitem.NewBigInteger(bi) }) 119 120 BigInt(buf.BinWriter, bi) 121 require.NoError(t, buf.Err) 122 123 expected := make([]byte, 33) 124 expected[0] = byte(opcode.PUSHINT256) 125 expected[32] = 0x80 126 require.Equal(t, expected, buf.Bytes()) 127 }) 128 t.Run("biggest positive number plus 1", func(t *testing.T) { 129 buf := io.NewBufBinWriter() 130 bi := big.NewInt(1) 131 bi.Lsh(bi, 255) 132 133 // sanity check 134 require.Panics(t, func() { stackitem.NewBigInteger(bi) }) 135 136 BigInt(buf.BinWriter, bi) 137 require.Error(t, buf.Err) 138 139 t.Run("do not clear previous error", func(t *testing.T) { 140 buf.Reset() 141 expected := errors.New("expected") 142 buf.Err = expected 143 BigInt(buf.BinWriter, bi) 144 require.Equal(t, expected, buf.Err) 145 }) 146 }) 147 t.Run("smallest negative number minus 1", func(t *testing.T) { 148 buf := io.NewBufBinWriter() 149 bi := big.NewInt(-1) 150 bi.Lsh(bi, 255) 151 bi.Sub(bi, big.NewInt(1)) 152 153 // sanity check 154 require.Panics(t, func() { stackitem.NewBigInteger(bi) }) 155 156 BigInt(buf.BinWriter, bi) 157 require.Error(t, buf.Err) 158 }) 159 } 160 161 func getSlice(n int) []byte { 162 data := make([]byte, n) 163 for i := range data { 164 data[i] = byte(i) 165 } 166 167 return data 168 } 169 170 func TestBytes(t *testing.T) { 171 t.Run("small slice", func(t *testing.T) { 172 buf := io.NewBufBinWriter() 173 Bytes(buf.BinWriter, []byte{0, 1, 2, 3}) 174 175 result := buf.Bytes() 176 assert.EqualValues(t, opcode.PUSHDATA1, result[0]) 177 assert.EqualValues(t, 4, result[1]) 178 assert.EqualValues(t, []byte{0, 1, 2, 3}, result[2:]) 179 }) 180 181 t.Run("slice with len <= 255", func(t *testing.T) { 182 const size = 200 183 184 buf := io.NewBufBinWriter() 185 Bytes(buf.BinWriter, getSlice(size)) 186 187 result := buf.Bytes() 188 assert.EqualValues(t, opcode.PUSHDATA1, result[0]) 189 assert.EqualValues(t, size, result[1]) 190 assert.Equal(t, getSlice(size), result[2:]) 191 }) 192 193 t.Run("slice with len <= 65535", func(t *testing.T) { 194 const size = 60000 195 196 buf := io.NewBufBinWriter() 197 Bytes(buf.BinWriter, getSlice(size)) 198 199 result := buf.Bytes() 200 assert.EqualValues(t, opcode.PUSHDATA2, result[0]) 201 assert.EqualValues(t, size, binary.LittleEndian.Uint16(result[1:3])) 202 assert.Equal(t, getSlice(size), result[3:]) 203 }) 204 205 t.Run("slice with len > 65535", func(t *testing.T) { 206 const size = 100000 207 208 buf := io.NewBufBinWriter() 209 Bytes(buf.BinWriter, getSlice(size)) 210 211 result := buf.Bytes() 212 assert.EqualValues(t, opcode.PUSHDATA4, result[0]) 213 assert.EqualValues(t, size, binary.LittleEndian.Uint32(result[1:5])) 214 assert.Equal(t, getSlice(size), result[5:]) 215 }) 216 } 217 218 func TestEmitArray(t *testing.T) { 219 t.Run("good", func(t *testing.T) { 220 buf := io.NewBufBinWriter() 221 var p160 *util.Uint160 222 var p256 *util.Uint256 223 u160 := util.Uint160{1, 2, 3} 224 u256 := util.Uint256{1, 2, 3} 225 veryBig := new(big.Int).SetUint64(math.MaxUint64) 226 veryBig.Add(veryBig, big.NewInt(1)) 227 Array(buf.BinWriter, 228 uint64(math.MaxUint64), 229 uint(math.MaxUint32), // don't use MaxUint to keep test results the same throughout all platforms. 230 stackitem.NewMapWithValue([]stackitem.MapElement{ 231 { 232 Key: stackitem.Make(1), 233 Value: stackitem.Make("str1"), 234 }, 235 { 236 Key: stackitem.Make(2), 237 Value: stackitem.Make("str2"), 238 }, 239 }), 240 stackitem.NewStruct([]stackitem.Item{ 241 stackitem.Make(4), 242 stackitem.Make("str"), 243 }), 244 &ConvertibleStruct{ 245 SomeInt: 5, 246 SomeString: "str", 247 }, 248 stackitem.Make(5), 249 stackitem.Make("str"), 250 stackitem.NewArray([]stackitem.Item{ 251 stackitem.Make(true), 252 stackitem.Make("str"), 253 }), 254 p160, p256, &u160, &u256, u160, u256, big.NewInt(0), veryBig, 255 []any{int64(1), int64(2)}, nil, int64(1), "str", false, true, []byte{0xCA, 0xFE}) 256 require.NoError(t, buf.Err) 257 258 res := buf.Bytes() 259 assert.EqualValues(t, opcode.PUSHDATA1, res[0]) 260 assert.EqualValues(t, 2, res[1]) 261 assert.EqualValues(t, []byte{0xCA, 0xFE}, res[2:4]) 262 assert.EqualValues(t, opcode.PUSHT, res[4]) 263 assert.EqualValues(t, opcode.PUSHF, res[5]) 264 assert.EqualValues(t, opcode.PUSHDATA1, res[6]) 265 assert.EqualValues(t, 3, res[7]) 266 assert.EqualValues(t, []byte("str"), res[8:11]) 267 assert.EqualValues(t, opcode.PUSH1, res[11]) 268 assert.EqualValues(t, opcode.PUSHNULL, res[12]) 269 assert.EqualValues(t, opcode.PUSH2, res[13]) 270 assert.EqualValues(t, opcode.PUSH1, res[14]) 271 assert.EqualValues(t, opcode.PUSH2, res[15]) 272 assert.EqualValues(t, opcode.PACK, res[16]) 273 assert.EqualValues(t, opcode.PUSHINT128, res[17]) 274 assert.EqualValues(t, veryBig, bigint.FromBytes(res[18:34])) 275 assert.EqualValues(t, opcode.PUSH0, res[34]) 276 assert.EqualValues(t, opcode.PUSHDATA1, res[35]) 277 assert.EqualValues(t, 32, res[36]) 278 assert.EqualValues(t, u256.BytesBE(), res[37:69]) 279 assert.EqualValues(t, opcode.PUSHDATA1, res[69]) 280 assert.EqualValues(t, 20, res[70]) 281 assert.EqualValues(t, u160.BytesBE(), res[71:91]) 282 assert.EqualValues(t, opcode.PUSHDATA1, res[91]) 283 assert.EqualValues(t, 32, res[92]) 284 assert.EqualValues(t, u256.BytesBE(), res[93:125]) 285 assert.EqualValues(t, opcode.PUSHDATA1, res[125]) 286 assert.EqualValues(t, 20, res[126]) 287 assert.EqualValues(t, u160.BytesBE(), res[127:147]) 288 assert.EqualValues(t, opcode.PUSHNULL, res[147]) 289 assert.EqualValues(t, opcode.PUSHNULL, res[148]) 290 // Array of two stackitems: 291 assert.EqualValues(t, opcode.PUSHDATA1, res[149]) 292 assert.EqualValues(t, 3, res[150]) 293 assert.EqualValues(t, []byte("str"), res[151:154]) 294 assert.EqualValues(t, opcode.PUSHT, res[154]) 295 assert.EqualValues(t, opcode.PUSH2, res[155]) 296 assert.EqualValues(t, opcode.PACK, res[156]) 297 // ByteString stackitem ("str"): 298 assert.EqualValues(t, opcode.PUSHDATA1, res[157]) 299 assert.EqualValues(t, 3, res[158]) 300 assert.EqualValues(t, []byte("str"), res[159:162]) 301 // Integer stackitem (5): 302 assert.EqualValues(t, opcode.PUSH5, res[162]) 303 // Convertible struct: 304 assert.EqualValues(t, opcode.PUSHDATA1, res[163]) 305 assert.EqualValues(t, 3, res[164]) 306 assert.EqualValues(t, []byte("str"), res[165:168]) 307 assert.EqualValues(t, opcode.PUSH5, res[168]) 308 assert.EqualValues(t, opcode.PUSH2, res[169]) 309 assert.EqualValues(t, opcode.PACK, res[170]) 310 // Struct stackitem (4, "str") 311 assert.EqualValues(t, opcode.PUSHDATA1, res[171]) 312 assert.EqualValues(t, 3, res[172]) 313 assert.EqualValues(t, []byte("str"), res[173:176]) 314 assert.EqualValues(t, opcode.PUSH4, res[176]) 315 assert.EqualValues(t, opcode.PUSH2, res[177]) 316 assert.EqualValues(t, opcode.PACKSTRUCT, res[178]) 317 // Map stackitem (1:"str1", 2:"str2") 318 assert.EqualValues(t, opcode.PUSHDATA1, res[179]) 319 assert.EqualValues(t, 4, res[180]) 320 assert.EqualValues(t, []byte("str2"), res[181:185]) 321 assert.EqualValues(t, opcode.PUSH2, res[185]) 322 assert.EqualValues(t, opcode.PUSHDATA1, res[186]) 323 assert.EqualValues(t, 4, res[187]) 324 assert.EqualValues(t, []byte("str1"), res[188:192]) 325 assert.EqualValues(t, opcode.PUSH1, res[192]) 326 assert.EqualValues(t, opcode.PUSH2, res[193]) 327 assert.EqualValues(t, opcode.PACKMAP, res[194]) 328 // uint (MaxUint32) 329 assert.EqualValues(t, opcode.PUSHINT64, res[195]) 330 assert.EqualValues(t, []byte{ 331 0xff, 0xff, 0xff, 0xff, 332 0, 0, 0, 0, 333 }, res[196:204]) 334 // uint64 (MaxUint64) 335 assert.EqualValues(t, opcode.PUSHINT128, res[204]) 336 assert.EqualValues(t, []byte{ 337 0xff, 0xff, 0xff, 0xff, 338 0xff, 0xff, 0xff, 0xff, 339 0, 0, 0, 0, 340 0, 0, 0, 0}, res[205:221]) 341 342 // Values packing: 343 assert.EqualValues(t, opcode.PUSHINT8, res[221]) 344 assert.EqualValues(t, byte(23), res[222]) 345 assert.EqualValues(t, opcode.PACK, res[223]) 346 347 // Overall script length: 348 assert.EqualValues(t, 224, len(res)) 349 }) 350 351 t.Run("empty", func(t *testing.T) { 352 buf := io.NewBufBinWriter() 353 Array(buf.BinWriter) 354 require.NoError(t, buf.Err) 355 assert.EqualValues(t, []byte{byte(opcode.NEWARRAY0)}, buf.Bytes()) 356 }) 357 358 t.Run("invalid type", func(t *testing.T) { 359 buf := io.NewBufBinWriter() 360 Array(buf.BinWriter, struct{}{}) 361 require.Error(t, buf.Err) 362 }) 363 } 364 365 func TestEmitBool(t *testing.T) { 366 buf := io.NewBufBinWriter() 367 Bool(buf.BinWriter, true) 368 Bool(buf.BinWriter, false) 369 result := buf.Bytes() 370 assert.EqualValues(t, opcode.PUSHT, result[0]) 371 assert.EqualValues(t, opcode.PUSHF, result[1]) 372 } 373 374 func TestEmitOpcode(t *testing.T) { 375 w := io.NewBufBinWriter() 376 Opcodes(w.BinWriter, opcode.PUSH1, opcode.NEWMAP) 377 result := w.Bytes() 378 assert.Equal(t, result, []byte{byte(opcode.PUSH1), byte(opcode.NEWMAP)}) 379 } 380 381 func TestEmitString(t *testing.T) { 382 buf := io.NewBufBinWriter() 383 str := "City Of Zion" 384 String(buf.BinWriter, str) 385 assert.Equal(t, buf.Len(), len(str)+2) 386 assert.Equal(t, buf.Bytes()[2:], []byte(str)) 387 } 388 389 func TestEmitSyscall(t *testing.T) { 390 syscalls := []string{ 391 interopnames.SystemRuntimeLog, 392 interopnames.SystemRuntimeNotify, 393 "System.Runtime.Whatever", 394 } 395 396 buf := io.NewBufBinWriter() 397 for _, syscall := range syscalls { 398 Syscall(buf.BinWriter, syscall) 399 result := buf.Bytes() 400 assert.Equal(t, 5, len(result)) 401 assert.Equal(t, opcode.Opcode(result[0]), opcode.SYSCALL) 402 assert.Equal(t, binary.LittleEndian.Uint32(result[1:]), interopnames.ToID([]byte(syscall))) 403 buf.Reset() 404 } 405 406 t.Run("empty syscall", func(t *testing.T) { 407 buf := io.NewBufBinWriter() 408 Syscall(buf.BinWriter, "") 409 assert.Error(t, buf.Err) 410 }) 411 412 t.Run("empty syscall after error", func(t *testing.T) { 413 buf := io.NewBufBinWriter() 414 err := errors.New("first error") 415 416 buf.Err = err 417 Syscall(buf.BinWriter, "") 418 assert.Equal(t, err, buf.Err) 419 }) 420 } 421 422 func TestJmp(t *testing.T) { 423 const label = 0x23 424 425 t.Run("correct", func(t *testing.T) { 426 ops := []opcode.Opcode{opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL} 427 for i := range ops { 428 t.Run(ops[i].String(), func(t *testing.T) { 429 buf := io.NewBufBinWriter() 430 Jmp(buf.BinWriter, ops[i], label) 431 assert.NoError(t, buf.Err) 432 433 result := buf.Bytes() 434 assert.EqualValues(t, ops[i], result[0]) 435 assert.EqualValues(t, 0x23, binary.LittleEndian.Uint16(result[1:])) 436 }) 437 } 438 }) 439 440 t.Run("not a jump instruction", func(t *testing.T) { 441 buf := io.NewBufBinWriter() 442 Jmp(buf.BinWriter, opcode.ABS, label) 443 assert.Error(t, buf.Err) 444 }) 445 446 t.Run("not a jump after error", func(t *testing.T) { 447 buf := io.NewBufBinWriter() 448 err := errors.New("first error") 449 450 buf.Err = err 451 Jmp(buf.BinWriter, opcode.ABS, label) 452 assert.Error(t, buf.Err) 453 }) 454 } 455 456 func TestEmitCall(t *testing.T) { 457 buf := io.NewBufBinWriter() 458 Call(buf.BinWriter, opcode.JMP, 100) 459 result := buf.Bytes() 460 assert.Equal(t, opcode.Opcode(result[0]), opcode.JMP) 461 label := binary.LittleEndian.Uint16(result[1:3]) 462 assert.Equal(t, label, uint16(100)) 463 } 464 465 func TestEmitStackitem(t *testing.T) { 466 t.Run("good", func(t *testing.T) { 467 buf := io.NewBufBinWriter() 468 itms := []stackitem.Item{ 469 stackitem.Make(true), 470 stackitem.Make(false), 471 stackitem.Make(5), 472 stackitem.Make("str"), 473 stackitem.Make([]stackitem.Item{ 474 stackitem.Make(true), 475 stackitem.Make([]stackitem.Item{ 476 stackitem.Make(1), 477 stackitem.Make("str"), 478 }), 479 }), 480 stackitem.NewStruct([]stackitem.Item{ 481 stackitem.Make(true), 482 stackitem.Make(7), 483 }), 484 stackitem.NewMapWithValue([]stackitem.MapElement{ 485 { 486 Key: stackitem.Make(7), 487 Value: stackitem.Make("str1"), 488 }, 489 { 490 Key: stackitem.Make(8), 491 Value: stackitem.Make("str2"), 492 }, 493 }), 494 stackitem.Null{}, 495 } 496 for _, si := range itms { 497 StackItem(buf.BinWriter, si) 498 } 499 require.NoError(t, buf.Err) 500 res := buf.Bytes() 501 502 // Single values: 503 assert.EqualValues(t, opcode.PUSHT, res[0]) 504 assert.EqualValues(t, opcode.PUSHF, res[1]) 505 assert.EqualValues(t, opcode.PUSH5, res[2]) 506 assert.EqualValues(t, opcode.PUSHDATA1, res[3]) 507 assert.EqualValues(t, 3, res[4]) 508 assert.EqualValues(t, []byte("str"), res[5:8]) 509 // Nested array: 510 assert.EqualValues(t, opcode.PUSHDATA1, res[8]) 511 assert.EqualValues(t, 3, res[9]) 512 assert.EqualValues(t, []byte("str"), res[10:13]) 513 assert.EqualValues(t, opcode.PUSH1, res[13]) 514 assert.EqualValues(t, opcode.PUSH2, res[14]) 515 assert.EqualValues(t, opcode.PACK, res[15]) 516 assert.EqualValues(t, opcode.PUSHT, res[16]) 517 assert.EqualValues(t, opcode.PUSH2, res[17]) 518 assert.EqualValues(t, opcode.PACK, res[18]) 519 // Struct (true, 7): 520 assert.EqualValues(t, opcode.PUSH7, res[19]) 521 assert.EqualValues(t, opcode.PUSHT, res[20]) 522 assert.EqualValues(t, opcode.PUSH2, res[21]) 523 assert.EqualValues(t, opcode.PACKSTRUCT, res[22]) 524 // Map (7:"str1", 8:"str2"): 525 assert.EqualValues(t, opcode.PUSHDATA1, res[23]) 526 assert.EqualValues(t, 4, res[24]) 527 assert.EqualValues(t, []byte("str2"), res[25:29]) 528 assert.EqualValues(t, opcode.PUSH8, res[29]) 529 assert.EqualValues(t, opcode.PUSHDATA1, res[30]) 530 assert.EqualValues(t, 4, res[31]) 531 assert.EqualValues(t, []byte("str1"), res[32:36]) 532 assert.EqualValues(t, opcode.PUSH7, res[36]) 533 assert.EqualValues(t, opcode.PUSH2, res[37]) 534 assert.EqualValues(t, opcode.PACKMAP, res[38]) 535 // Null: 536 assert.EqualValues(t, opcode.PUSHNULL, res[39]) 537 538 // Overall script length: 539 require.Equal(t, 40, len(res)) 540 }) 541 542 t.Run("unsupported", func(t *testing.T) { 543 itms := []stackitem.Item{ 544 stackitem.NewInterop(nil), 545 stackitem.NewPointer(123, []byte{123}), 546 } 547 for _, si := range itms { 548 buf := io.NewBufBinWriter() 549 StackItem(buf.BinWriter, si) 550 require.Error(t, buf.Err) 551 } 552 }) 553 554 t.Run("invalid any", func(t *testing.T) { 555 buf := io.NewBufBinWriter() 556 StackItem(buf.BinWriter, StrangeStackItem{}) 557 actualErr := buf.Err 558 require.Error(t, actualErr) 559 require.True(t, strings.Contains(actualErr.Error(), "only nil value supported"), actualErr.Error()) 560 }) 561 } 562 563 type StrangeStackItem struct{} 564 565 var _ = stackitem.Item(StrangeStackItem{}) 566 567 func (StrangeStackItem) Value() any { 568 return struct{}{} 569 } 570 func (StrangeStackItem) Type() stackitem.Type { 571 return stackitem.AnyT 572 } 573 func (StrangeStackItem) String() string { 574 panic("TODO") 575 } 576 func (StrangeStackItem) Dup() stackitem.Item { 577 panic("TODO") 578 } 579 func (StrangeStackItem) TryBool() (bool, error) { 580 panic("TODO") 581 } 582 func (StrangeStackItem) TryBytes() ([]byte, error) { 583 panic("TODO") 584 } 585 func (StrangeStackItem) TryInteger() (*big.Int, error) { 586 panic("TODO") 587 } 588 func (StrangeStackItem) Equals(stackitem.Item) bool { 589 panic("TODO") 590 } 591 func (StrangeStackItem) Convert(stackitem.Type) (stackitem.Item, error) { 592 panic("TODO") 593 } 594 595 type ConvertibleStruct struct { 596 SomeInt int 597 SomeString string 598 err error 599 } 600 601 var _ = stackitem.Convertible(&ConvertibleStruct{}) 602 603 func (s *ConvertibleStruct) ToStackItem() (stackitem.Item, error) { 604 if s.err != nil { 605 return nil, s.err 606 } 607 return stackitem.NewArray([]stackitem.Item{ 608 stackitem.Make(s.SomeInt), 609 stackitem.Make(s.SomeString), 610 }), nil 611 } 612 613 func (s *ConvertibleStruct) FromStackItem(si stackitem.Item) error { 614 panic("TODO") 615 } 616 617 func TestEmitConvertible(t *testing.T) { 618 t.Run("good", func(t *testing.T) { 619 buf := io.NewBufBinWriter() 620 str := &ConvertibleStruct{ 621 SomeInt: 5, 622 SomeString: "str", 623 } 624 Convertible(buf.BinWriter, str) 625 require.NoError(t, buf.Err) 626 res := buf.Bytes() 627 628 // The struct itself: 629 assert.EqualValues(t, opcode.PUSHDATA1, res[0]) 630 assert.EqualValues(t, 3, res[1]) 631 assert.EqualValues(t, []byte("str"), res[2:5]) 632 assert.EqualValues(t, opcode.PUSH5, res[5]) 633 assert.EqualValues(t, opcode.PUSH2, res[6]) 634 assert.EqualValues(t, opcode.PACK, res[7]) 635 636 // Overall length: 637 assert.EqualValues(t, 8, len(res)) 638 }) 639 640 t.Run("error on conversion", func(t *testing.T) { 641 buf := io.NewBufBinWriter() 642 expectedErr := errors.New("error on conversion") 643 str := &ConvertibleStruct{ 644 err: expectedErr, 645 } 646 Convertible(buf.BinWriter, str) 647 actualErr := buf.Err 648 require.Error(t, actualErr) 649 require.ErrorIs(t, actualErr, expectedErr) 650 }) 651 }