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  }