github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/interop_test.go (about)

     1  package compiler_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/big"
     7  	"strconv"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/nspcc-dev/neo-go/internal/fakechain"
    12  	"github.com/nspcc-dev/neo-go/pkg/compiler"
    13  	"github.com/nspcc-dev/neo-go/pkg/config"
    14  	"github.com/nspcc-dev/neo-go/pkg/core"
    15  	"github.com/nspcc-dev/neo-go/pkg/core/dao"
    16  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
    17  	"github.com/nspcc-dev/neo-go/pkg/core/native"
    18  	"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
    19  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    20  	"github.com/nspcc-dev/neo-go/pkg/core/storage"
    21  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    22  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    23  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    24  	"github.com/nspcc-dev/neo-go/pkg/encoding/base58"
    25  	cinterop "github.com/nspcc-dev/neo-go/pkg/interop"
    26  	"github.com/nspcc-dev/neo-go/pkg/neotest"
    27  	"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
    28  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    29  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    30  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    31  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    32  	"github.com/nspcc-dev/neo-go/pkg/util"
    33  	"github.com/nspcc-dev/neo-go/pkg/vm"
    34  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    35  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    36  	"github.com/stretchr/testify/require"
    37  	"go.uber.org/zap/zaptest"
    38  )
    39  
    40  func TestTypeConstantSize(t *testing.T) {
    41  	src := `package foo
    42  	import "github.com/nspcc-dev/neo-go/pkg/interop"
    43  	var a %T // type declaration is always ok
    44  	func Main() any {
    45  		return %#v
    46  	}`
    47  
    48  	t.Run("Hash160", func(t *testing.T) {
    49  		t.Run("good", func(t *testing.T) {
    50  			a := make(cinterop.Hash160, smartcontract.Hash160Len)
    51  			src := fmt.Sprintf(src, a, a)
    52  			eval(t, src, []byte(a))
    53  		})
    54  		t.Run("bad", func(t *testing.T) {
    55  			a := make(cinterop.Hash160, 19)
    56  			src := fmt.Sprintf(src, a, a)
    57  			_, err := compiler.Compile("foo.go", strings.NewReader(src))
    58  			require.Error(t, err)
    59  		})
    60  	})
    61  	t.Run("Hash256", func(t *testing.T) {
    62  		t.Run("good", func(t *testing.T) {
    63  			a := make(cinterop.Hash256, smartcontract.Hash256Len)
    64  			src := fmt.Sprintf(src, a, a)
    65  			eval(t, src, []byte(a))
    66  		})
    67  		t.Run("bad", func(t *testing.T) {
    68  			a := make(cinterop.Hash256, 31)
    69  			src := fmt.Sprintf(src, a, a)
    70  			_, err := compiler.Compile("foo.go", strings.NewReader(src))
    71  			require.Error(t, err)
    72  		})
    73  	})
    74  }
    75  
    76  func TestAddressToHash160BuiltinConversion(t *testing.T) {
    77  	a := "NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN"
    78  	h, err := address.StringToUint160(a)
    79  	require.NoError(t, err)
    80  	a2 := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8"
    81  	addr2, err := address.StringToUint160(a2)
    82  	require.NoError(t, err)
    83  	t.Run("builtin conversion", func(t *testing.T) {
    84  		src := `package foo
    85  		import (
    86  			"github.com/nspcc-dev/neo-go/pkg/interop"
    87  			"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
    88  		)
    89  		var addr = address.ToHash160("` + a + `")
    90  		func Main() interop.Hash160 {
    91  			return addr
    92  		}`
    93  		prog := eval(t, src, h.BytesBE())
    94  		// Address BE bytes expected to be present at program, which indicates that address conversion
    95  		// was performed at compile-time.
    96  		require.True(t, strings.Contains(string(prog), string(h.BytesBE())))
    97  		// On the contrary, there should be no address string.
    98  		require.False(t, strings.Contains(string(prog), a))
    99  	})
   100  	t.Run("generate code", func(t *testing.T) {
   101  		src := `package foo
   102  		import (
   103  			"github.com/nspcc-dev/neo-go/pkg/interop"
   104  			"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
   105  		)
   106  		var addr = "` + a + `"
   107  		func Main() interop.Hash160 {
   108  			return address.ToHash160(addr)
   109  		}`
   110  		// Error on CALLT (std.Base58CheckDecode - method of StdLib native contract) is expected, which means
   111  		// that address.ToHash160 code was honestly generated by the compiler without any optimisations.
   112  		prog := evalWithError(t, src, "(CALLT): runtime error: invalid memory address or nil pointer dereference")
   113  		// Address BE bytes expected not to be present at program, which indicates that address conversion
   114  		// was not performed at compile-time.
   115  		require.False(t, strings.Contains(string(prog), string(h.BytesBE())))
   116  		// On the contrary, there should be an address string.
   117  		require.True(t, strings.Contains(string(prog), a))
   118  	})
   119  	t.Run("AliasPackage", func(t *testing.T) {
   120  		src := `
   121  		package foo
   122  		import ad "github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
   123  		func Main() []byte {
   124  			addr1 := ad.ToHash160("` + a + `")
   125  			addr2 := ad.ToHash160("` + a2 + `")
   126  			sum := append(addr1, addr2...)
   127  			return sum
   128  		}`
   129  		eval(t, src, append(h.BytesBE(), addr2.BytesBE()...))
   130  	})
   131  }
   132  
   133  func TestInvokeAddressToFromHash160(t *testing.T) {
   134  	a := "NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN"
   135  	h, err := address.StringToUint160(a)
   136  	require.NoError(t, err)
   137  
   138  	bc, acc := chain.NewSingle(t)
   139  	e := neotest.NewExecutor(t, bc, acc, acc)
   140  	src := `package foo
   141  		import (
   142  			"github.com/nspcc-dev/neo-go/pkg/interop"
   143  			"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
   144  		)
   145  		const addr = "` + a + `"
   146  		func ToHash160(a string) interop.Hash160 {
   147  			return address.ToHash160(a)
   148  		}
   149  		func ToHash160AtCompileTime() interop.Hash160 {
   150  			return address.ToHash160(addr)
   151  		}
   152  		func FromHash160(hash interop.Hash160) string {
   153  			return address.FromHash160(hash)
   154  		}`
   155  	ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{Name: "Helper"})
   156  	e.DeployContract(t, ctr, nil)
   157  	c := e.CommitteeInvoker(ctr.Hash)
   158  
   159  	t.Run("ToHash160", func(t *testing.T) {
   160  		t.Run("invalid address length", func(t *testing.T) {
   161  			c.InvokeFail(t, "invalid address length", "toHash160", base58.CheckEncode(make([]byte, util.Uint160Size+1+1)))
   162  		})
   163  		t.Run("invalid prefix", func(t *testing.T) {
   164  			c.InvokeFail(t, "invalid address prefix", "toHash160", base58.CheckEncode(append([]byte{address.NEO2Prefix}, h.BytesBE()...)))
   165  		})
   166  		t.Run("good", func(t *testing.T) {
   167  			c.Invoke(t, stackitem.NewBuffer(h.BytesBE()), "toHash160", a)
   168  		})
   169  	})
   170  	t.Run("ToHash160Constant", func(t *testing.T) {
   171  		t.Run("good", func(t *testing.T) {
   172  			c.Invoke(t, stackitem.NewBuffer(h.BytesBE()), "toHash160AtCompileTime")
   173  		})
   174  	})
   175  	t.Run("FromHash160", func(t *testing.T) {
   176  		t.Run("good", func(t *testing.T) {
   177  			c.Invoke(t, stackitem.NewByteArray([]byte(a)), "fromHash160", h.BytesBE())
   178  		})
   179  		t.Run("invalid length", func(t *testing.T) {
   180  			c.InvokeFail(t, "invalid Hash160 length", "fromHash160", h.BytesBE()[:15])
   181  		})
   182  	})
   183  }
   184  
   185  func TestAbort(t *testing.T) {
   186  	src := `package foo
   187  	import "github.com/nspcc-dev/neo-go/pkg/interop/util"
   188  	func Main() int {
   189  		util.Abort()
   190  		return 1
   191  	}`
   192  	v := vmAndCompile(t, src)
   193  	require.Error(t, v.Run())
   194  	require.True(t, v.HasFailed())
   195  }
   196  
   197  func TestAbortMsg(t *testing.T) {
   198  	src := `package foo
   199  	import "github.com/nspcc-dev/neo-go/pkg/interop/util"
   200  	func Main() int {
   201  		util.AbortMsg("some message")
   202  		return 1
   203  	}`
   204  	v := vmAndCompile(t, src)
   205  	err := v.Run()
   206  	require.Error(t, err)
   207  	require.True(t, v.HasFailed())
   208  	require.True(t, strings.Contains(err.Error(), "ABORTMSG is executed. Reason: some message"), err)
   209  }
   210  
   211  func TestAssert(t *testing.T) {
   212  	src := `package foo
   213  	import "github.com/nspcc-dev/neo-go/pkg/interop/util"
   214  	func Main(ok bool) int {
   215  		util.Assert(ok)
   216  		return 1
   217  	}`
   218  
   219  	// assert OK
   220  	evalWithArgs(t, src, nil, []stackitem.Item{stackitem.Make(true)}, big.NewInt(1))
   221  
   222  	// assert FALSE
   223  	v := vmAndCompile(t, src)
   224  	v.Estack().PushVal(false)
   225  	err := v.Run()
   226  	require.Error(t, err)
   227  	require.True(t, v.HasFailed())
   228  	require.True(t, strings.Contains(err.Error(), "ASSERT"), err)
   229  }
   230  
   231  func TestAssertMsg(t *testing.T) {
   232  	src := `package foo
   233  	import "github.com/nspcc-dev/neo-go/pkg/interop/util"
   234  	func Main(ok bool) int {
   235  		util.AssertMsg(ok, "some message")
   236  		return 1
   237  	}`
   238  
   239  	// assert OK
   240  	evalWithArgs(t, src, nil, []stackitem.Item{stackitem.Make(true)}, big.NewInt(1))
   241  
   242  	// assert FALSE
   243  	v := vmAndCompile(t, src)
   244  	v.Estack().PushVal(false)
   245  	err := v.Run()
   246  	require.Error(t, err)
   247  	require.True(t, v.HasFailed())
   248  	require.True(t, strings.Contains(err.Error(), "ASSERTMSG is executed with false result. Reason: some message"), err)
   249  }
   250  
   251  func TestCurrentSigners(t *testing.T) {
   252  	bc, acc := chain.NewSingle(t)
   253  	e := neotest.NewExecutor(t, bc, acc, acc)
   254  	src := `package foo
   255  		import (
   256  			"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
   257  			"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
   258  		)
   259  		func Main() []ledger.TransactionSigner {
   260  			return runtime.CurrentSigners()
   261  		}`
   262  	ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{Name: "Helper"})
   263  	e.DeployContract(t, ctr, nil)
   264  	c := e.CommitteeInvoker(ctr.Hash)
   265  
   266  	t.Run("non-empty", func(t *testing.T) {
   267  		expected := stackitem.NewArray([]stackitem.Item{
   268  			stackitem.NewArray([]stackitem.Item{
   269  				stackitem.NewByteArray(e.CommitteeHash.BytesBE()),
   270  				stackitem.NewBigInteger(big.NewInt(int64(transaction.Global))),
   271  				stackitem.NewArray([]stackitem.Item{}),
   272  				stackitem.NewArray([]stackitem.Item{}),
   273  				stackitem.NewArray([]stackitem.Item{}),
   274  			}),
   275  		})
   276  		c.Invoke(t, expected, "main")
   277  	})
   278  }
   279  
   280  func TestStdLib_StrLen(t *testing.T) {
   281  	bc, acc := chain.NewSingle(t)
   282  	e := neotest.NewExecutor(t, bc, acc, acc)
   283  	src := `package foo
   284  		import (
   285  			"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
   286  		)
   287  		func Main(s string) int {
   288  			return std.StrLen(s)
   289  		}`
   290  	ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{Name: "Helper"})
   291  	e.DeployContract(t, ctr, nil)
   292  	c := e.CommitteeInvoker(ctr.Hash)
   293  
   294  	expected := stackitem.Make(1)
   295  	c.Invoke(t, expected, "main", "🦆")
   296  	c.Invoke(t, expected, "main", "ã")
   297  	c.Invoke(t, expected, "main", "a")
   298  
   299  	expected = stackitem.Make(7)
   300  	c.Invoke(t, expected, "main", "abc 123")
   301  }
   302  
   303  func spawnVM(t *testing.T, ic *interop.Context, src string) *vm.VM {
   304  	b, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
   305  	require.NoError(t, err)
   306  	v := core.SpawnVM(ic)
   307  	invokeMethod(t, testMainIdent, b.Script, v, di)
   308  	v.LoadScriptWithFlags(b.Script, callflag.All)
   309  	return v
   310  }
   311  
   312  func TestAppCall(t *testing.T) {
   313  	srcDeep := `package foo
   314  	func Get42() int {
   315  		return 42
   316  	}`
   317  	barCtr, di, err := compiler.CompileWithOptions("bar.go", strings.NewReader(srcDeep), nil)
   318  	require.NoError(t, err)
   319  	mBar, err := di.ConvertToManifest(&compiler.Options{Name: "Bar"})
   320  	require.NoError(t, err)
   321  
   322  	barH := hash.Hash160(barCtr.Script)
   323  
   324  	srcInner := `package foo
   325  	import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   326  	import "github.com/nspcc-dev/neo-go/pkg/interop"
   327  	var a int = 3
   328  	func Main(a []byte, b []byte) []byte {
   329  		panic("Main was called")
   330  	}
   331  	func Append(a []byte, b []byte) []byte {
   332  		return append(a, b...)
   333  	}
   334  	func Add3(n int) int {
   335  		return a + n
   336  	}
   337  	func CallInner() int {
   338  		return contract.Call(%s, "get42", contract.All).(int)
   339  	}`
   340  	srcInner = fmt.Sprintf(srcInner,
   341  		fmt.Sprintf("%#v", cinterop.Hash160(barH.BytesBE())))
   342  
   343  	inner, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(srcInner), nil)
   344  	require.NoError(t, err)
   345  	m, err := di.ConvertToManifest(&compiler.Options{
   346  		Name: "Foo",
   347  		Permissions: []manifest.Permission{
   348  			*manifest.NewPermission(manifest.PermissionWildcard),
   349  		},
   350  	})
   351  	require.NoError(t, err)
   352  
   353  	ih := hash.Hash160(inner.Script)
   354  	var contractGetter = func(_ *dao.Simple, h util.Uint160) (*state.Contract, error) {
   355  		if h.Equals(ih) {
   356  			return &state.Contract{
   357  				ContractBase: state.ContractBase{
   358  					Hash:     ih,
   359  					NEF:      *inner,
   360  					Manifest: *m,
   361  				},
   362  			}, nil
   363  		} else if h.Equals(barH) {
   364  			return &state.Contract{
   365  				ContractBase: state.ContractBase{
   366  					Hash:     barH,
   367  					NEF:      *barCtr,
   368  					Manifest: *mBar,
   369  				},
   370  			}, nil
   371  		}
   372  		return nil, errors.New("not found")
   373  	}
   374  
   375  	fc := fakechain.NewFakeChain()
   376  	ic := interop.NewContext(trigger.Application, fc, dao.NewSimple(storage.NewMemoryStore(), false),
   377  		interop.DefaultBaseExecFee, native.DefaultStoragePrice, contractGetter, nil, nil, nil, nil, zaptest.NewLogger(t))
   378  
   379  	t.Run("valid script", func(t *testing.T) {
   380  		src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
   381  		v := spawnVM(t, ic, src)
   382  		require.NoError(t, v.Run())
   383  
   384  		assertResult(t, v, []byte{1, 2, 3, 4})
   385  	})
   386  
   387  	t.Run("callEx, valid", func(t *testing.T) {
   388  		src := getCallExScript(fmt.Sprintf("%#v", ih.BytesBE()), "contract.ReadStates|contract.AllowCall")
   389  		v := spawnVM(t, ic, src)
   390  		require.NoError(t, v.Run())
   391  
   392  		assertResult(t, v, big.NewInt(42))
   393  	})
   394  	t.Run("callEx, missing flags", func(t *testing.T) {
   395  		src := getCallExScript(fmt.Sprintf("%#v", ih.BytesBE()), "contract.NoneFlag")
   396  		v := spawnVM(t, ic, src)
   397  		require.Error(t, v.Run())
   398  	})
   399  
   400  	t.Run("missing script", func(t *testing.T) {
   401  		h := ih
   402  		h[0] = ^h[0]
   403  
   404  		src := getAppCallScript(fmt.Sprintf("%#v", h.BytesBE()))
   405  		v := spawnVM(t, ic, src)
   406  		require.Error(t, v.Run())
   407  	})
   408  
   409  	t.Run("convert from string constant", func(t *testing.T) {
   410  		src := `
   411  		package foo
   412  		import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   413  		const scriptHash = ` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `
   414  		func Main() []byte {
   415  			x := []byte{1, 2}
   416  			y := []byte{3, 4}
   417  			result := contract.Call([]byte(scriptHash), "append", contract.All, x, y)
   418  			return result.([]byte)
   419  		}
   420  		`
   421  
   422  		v := spawnVM(t, ic, src)
   423  		require.NoError(t, v.Run())
   424  
   425  		assertResult(t, v, []byte{1, 2, 3, 4})
   426  	})
   427  
   428  	t.Run("convert from var", func(t *testing.T) {
   429  		src := `
   430  		package foo
   431  		import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   432  		func Main() []byte {
   433  			x := []byte{1, 2}
   434  			y := []byte{3, 4}
   435  			var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `)
   436  			result := contract.Call(addr, "append", contract.All, x, y)
   437  			return result.([]byte)
   438  		}
   439  		`
   440  
   441  		v := spawnVM(t, ic, src)
   442  		require.NoError(t, v.Run())
   443  
   444  		assertResult(t, v, []byte{1, 2, 3, 4})
   445  	})
   446  
   447  	t.Run("InitializedGlobals", func(t *testing.T) {
   448  		src := `package foo
   449  		import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   450  		func Main() int {
   451  			var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `)
   452  			result := contract.Call(addr, "add3", contract.All, 39)
   453  			return result.(int)
   454  		}`
   455  
   456  		v := spawnVM(t, ic, src)
   457  		require.NoError(t, v.Run())
   458  
   459  		assertResult(t, v, big.NewInt(42))
   460  	})
   461  
   462  	t.Run("AliasPackage", func(t *testing.T) {
   463  		src := `package foo
   464  		import ee "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   465  		func Main() int {
   466  			var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `)
   467  			result := ee.Call(addr, "add3", ee.All, 39)
   468  			return result.(int)
   469  		}`
   470  		v := spawnVM(t, ic, src)
   471  		require.NoError(t, v.Run())
   472  		assertResult(t, v, big.NewInt(42))
   473  	})
   474  }
   475  
   476  func getAppCallScript(h string) string {
   477  	return `
   478  	package foo
   479  	import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   480  	func Main() []byte {
   481  		x := []byte{1, 2}
   482  		y := []byte{3, 4}
   483  		result := contract.Call(` + h + `, "append", contract.All, x, y)
   484  		return result.([]byte)
   485  	}
   486  	`
   487  }
   488  
   489  func getCallExScript(h string, flags string) string {
   490  	return `package foo
   491  	import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   492  	func Main() int {
   493  		result := contract.Call(` + h + `, "callInner", ` + flags + `)
   494  		return result.(int)
   495  	}`
   496  }
   497  
   498  func TestBuiltinDoesNotCompile(t *testing.T) {
   499  	src := `package foo
   500  	import "github.com/nspcc-dev/neo-go/pkg/interop/util"
   501  	func Main() bool {
   502  		a := 1
   503  		b := 2
   504  		return util.Equals(a, b)
   505  	}`
   506  
   507  	v := vmAndCompile(t, src)
   508  	ctx := v.Context()
   509  	retCount := 0
   510  	for op, _, err := ctx.Next(); err == nil; op, _, err = ctx.Next() {
   511  		if ctx.IP() >= len(ctx.Program()) {
   512  			break
   513  		}
   514  		if op == opcode.RET {
   515  			retCount++
   516  		}
   517  	}
   518  	require.Equal(t, 1, retCount)
   519  }
   520  
   521  func TestInteropPackage(t *testing.T) {
   522  	src := `package foo
   523  	import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/block"
   524  	func Main() int {
   525  		b := block.Block{}
   526  		a := block.GetTransactionCount(b)
   527  		return a
   528  	}`
   529  	eval(t, src, big.NewInt(42))
   530  }
   531  
   532  func TestBuiltinPackage(t *testing.T) {
   533  	src := `package foo
   534  	import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/util"
   535  	func Main() int {
   536  		if util.Equals(1, 2) { // always returns true
   537  			return 1
   538  		}
   539  		return 2
   540  	}`
   541  	eval(t, src, big.NewInt(1))
   542  }
   543  
   544  func TestLenForNil(t *testing.T) {
   545  	src := `
   546  	package foo
   547  	func Main() bool {
   548  		var a []int = nil
   549  		return len(a) == 0
   550  	}`
   551  
   552  	eval(t, src, true)
   553  }
   554  
   555  func TestCallTConversionErrors(t *testing.T) {
   556  	t.Run("variable hash", func(t *testing.T) {
   557  		src := `package foo
   558  		import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
   559  		func Main() int {
   560  			var hash string
   561  			return neogointernal.CallWithToken(hash, "method", 0).(int)
   562  		}`
   563  		_, err := compiler.Compile("foo.go", strings.NewReader(src))
   564  		require.Error(t, err)
   565  	})
   566  	t.Run("bad hash", func(t *testing.T) {
   567  		src := `package foo
   568  		import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
   569  		func Main() int {
   570  			return neogointernal.CallWithToken("badstring", "method", 0).(int)
   571  		}`
   572  		_, err := compiler.Compile("foo.go", strings.NewReader(src))
   573  		require.Error(t, err)
   574  	})
   575  	t.Run("variable method", func(t *testing.T) {
   576  		src := `package foo
   577  		import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
   578  		func Main() int {
   579  			var method string
   580  			return neogointernal.CallWithToken("\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\xa0\x73\x40\xef", method, 0).(int)
   581  		}`
   582  		_, err := compiler.Compile("foo.go", strings.NewReader(src))
   583  		require.Error(t, err)
   584  	})
   585  	t.Run("variable flags", func(t *testing.T) {
   586  		src := `package foo
   587  		import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
   588  		func Main() {
   589  			var flags int
   590  			neogointernal.CallWithTokenNoRet("\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\xa0\x73\x40\xef", "method", flags)
   591  		}`
   592  		_, err := compiler.Compile("foo.go", strings.NewReader(src))
   593  		require.Error(t, err)
   594  	})
   595  }
   596  
   597  func TestCallWithVersion(t *testing.T) {
   598  	bc, acc := chain.NewSingle(t)
   599  	e := neotest.NewExecutor(t, bc, acc, acc)
   600  	src := `package foo
   601  		import (
   602  			"github.com/nspcc-dev/neo-go/pkg/interop"
   603  			"github.com/nspcc-dev/neo-go/pkg/interop/contract"
   604  			util "github.com/nspcc-dev/neo-go/pkg/interop/lib/contract"
   605  		)
   606  		func CallWithVersion(hash interop.Hash160, version int, method string) any {
   607  			return util.CallWithVersion(hash, version, method, contract.All)
   608  		}`
   609  	ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{Name: "Helper"})
   610  	e.DeployContract(t, ctr, nil)
   611  	c := e.CommitteeInvoker(ctr.Hash)
   612  
   613  	policyH := nativehashes.PolicyContract
   614  	t.Run("good", func(t *testing.T) {
   615  		c.Invoke(t, e.Chain.GetBaseExecFee(), "callWithVersion", policyH.BytesBE(), 0, "getExecFeeFactor")
   616  	})
   617  	t.Run("unknown contract", func(t *testing.T) {
   618  		c.InvokeFail(t, "unknown contract", "callWithVersion", util.Uint160{1, 2, 3}.BytesBE(), 0, "getExecFeeFactor")
   619  	})
   620  	t.Run("invalid version", func(t *testing.T) {
   621  		c.InvokeFail(t, "contract version mismatch", "callWithVersion", policyH.BytesBE(), 1, "getExecFeeFactor")
   622  	})
   623  }
   624  
   625  func TestForcedNotifyArgumentsConversion(t *testing.T) {
   626  	const methodWithEllipsis = "withEllipsis"
   627  	const methodWithoutEllipsis = "withoutEllipsis"
   628  	check := func(t *testing.T, method string, targetSCParamTypes []smartcontract.ParamType, expectedVMParamTypes []stackitem.Type, noEventsCheck bool) {
   629  		bc, acc := chain.NewSingleWithCustomConfig(t, func(blockchain *config.Blockchain) {
   630  			blockchain.Hardforks = map[string]uint32{config.HFBasilisk.String(): 100500} // Disable runtime notifications check to reuse the same contract for different event parameter types.
   631  		})
   632  		e := neotest.NewExecutor(t, bc, acc, acc)
   633  		src := `package foo
   634  		import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
   635  		const arg4 = 4			// Const value.
   636  		func WithoutEllipsis() {
   637  			var arg0 int		// Default value.
   638  			var arg1 int = 1	// Initialized value.
   639  			arg2 := 2			// Short decl.
   640  			var arg3 int
   641  			arg3 = 3			// Declare first, change value afterwards.
   642  			runtime.Notify("withoutEllipsis", arg0, arg1, arg2, arg3, arg4, 5, f(6))	// The fifth argument is basic literal.
   643  		}
   644  		func WithEllipsis() {
   645  			arg := []any{0, 1, f(2), 3, 4, 5, 6}
   646  			runtime.Notify("withEllipsis", arg...)
   647  		}
   648  		func f(i int) int {
   649  			return i
   650  		}`
   651  		count := len(targetSCParamTypes)
   652  		if count != len(expectedVMParamTypes) {
   653  			t.Fatalf("parameters count mismatch: %d vs %d", count, len(expectedVMParamTypes))
   654  		}
   655  		scParams := make([]compiler.HybridParameter, len(targetSCParamTypes))
   656  		vmParams := make([]stackitem.Item, len(expectedVMParamTypes))
   657  		for i := range scParams {
   658  			scParams[i] = compiler.HybridParameter{Parameter: manifest.Parameter{
   659  				Name: strconv.Itoa(i),
   660  				Type: targetSCParamTypes[i],
   661  			}}
   662  			defaultValue := stackitem.NewBigInteger(big.NewInt(int64(i)))
   663  			var (
   664  				val stackitem.Item
   665  				err error
   666  			)
   667  			if expectedVMParamTypes[i] == stackitem.IntegerT {
   668  				val = defaultValue
   669  			} else {
   670  				val, err = defaultValue.Convert(expectedVMParamTypes[i]) // exactly the same conversion should be emitted by compiler and performed by the contract code.
   671  				require.NoError(t, err)
   672  			}
   673  			vmParams[i] = val
   674  		}
   675  		ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{
   676  			Name: "Helper",
   677  			ContractEvents: []compiler.HybridEvent{
   678  				{
   679  					Name:       methodWithoutEllipsis,
   680  					Parameters: scParams,
   681  				},
   682  				{
   683  					Name:       methodWithEllipsis,
   684  					Parameters: scParams,
   685  				},
   686  			},
   687  			NoEventsCheck: noEventsCheck,
   688  		})
   689  		e.DeployContract(t, ctr, nil)
   690  		c := e.CommitteeInvoker(ctr.Hash)
   691  
   692  		t.Run(method, func(t *testing.T) {
   693  			h := c.Invoke(t, stackitem.Null{}, method)
   694  			aer := c.GetTxExecResult(t, h)
   695  			require.Equal(t, 1, len(aer.Events))
   696  			require.Equal(t, stackitem.NewArray(vmParams), aer.Events[0].Item)
   697  		})
   698  	}
   699  	checkSingleType := func(t *testing.T, method string, targetSCEventType smartcontract.ParamType, expectedVMType stackitem.Type, noEventsCheck ...bool) {
   700  		count := 7
   701  		scParams := make([]smartcontract.ParamType, count)
   702  		vmParams := make([]stackitem.Type, count)
   703  		for i := range scParams {
   704  			scParams[i] = targetSCEventType
   705  			vmParams[i] = expectedVMType
   706  		}
   707  		var noEvents bool
   708  		if len(noEventsCheck) > 0 {
   709  			noEvents = noEventsCheck[0]
   710  		}
   711  		check(t, method, scParams, vmParams, noEvents)
   712  	}
   713  
   714  	t.Run("good, single type, default values", func(t *testing.T) {
   715  		checkSingleType(t, methodWithoutEllipsis, smartcontract.IntegerType, stackitem.IntegerT)
   716  	})
   717  	t.Run("good, single type, conversion to BooleanT", func(t *testing.T) {
   718  		checkSingleType(t, methodWithoutEllipsis, smartcontract.BoolType, stackitem.BooleanT)
   719  	})
   720  	t.Run("good, single type, Hash160Type->ByteArray", func(t *testing.T) {
   721  		checkSingleType(t, methodWithoutEllipsis, smartcontract.Hash160Type, stackitem.ByteArrayT)
   722  	})
   723  	t.Run("good, single type, Hash256Type->ByteArray", func(t *testing.T) {
   724  		checkSingleType(t, methodWithoutEllipsis, smartcontract.Hash256Type, stackitem.ByteArrayT)
   725  	})
   726  	t.Run("good, single type, Signature->ByteArray", func(t *testing.T) {
   727  		checkSingleType(t, methodWithoutEllipsis, smartcontract.SignatureType, stackitem.ByteArrayT)
   728  	})
   729  	t.Run("good, single type, String->ByteArray", func(t *testing.T) {
   730  		checkSingleType(t, methodWithoutEllipsis, smartcontract.StringType, stackitem.ByteArrayT) // Special case, runtime.Notify will convert any Buffer to ByteArray.
   731  	})
   732  	t.Run("good, single type, PublicKeyType->ByteArray", func(t *testing.T) {
   733  		checkSingleType(t, methodWithoutEllipsis, smartcontract.PublicKeyType, stackitem.ByteArrayT)
   734  	})
   735  	t.Run("good, single type, AnyType->do not change initial type", func(t *testing.T) {
   736  		checkSingleType(t, methodWithoutEllipsis, smartcontract.AnyType, stackitem.IntegerT) // Special case, compiler should leave the type "as is" and do not emit conversion code.
   737  	})
   738  	// Test for InteropInterface->... is missing, because we don't enforce conversion to stackitem.InteropInterface,
   739  	// but compiler still checks these notifications against expected manifest.
   740  	t.Run("good, multiple types, check the conversion order", func(t *testing.T) {
   741  		check(t, methodWithoutEllipsis, []smartcontract.ParamType{
   742  			smartcontract.IntegerType,
   743  			smartcontract.BoolType,
   744  			smartcontract.ByteArrayType,
   745  			smartcontract.PublicKeyType,
   746  			smartcontract.Hash160Type,
   747  			smartcontract.AnyType, // leave initial type
   748  			smartcontract.StringType,
   749  		}, []stackitem.Type{
   750  			stackitem.IntegerT,
   751  			stackitem.BooleanT,
   752  			stackitem.ByteArrayT,
   753  			stackitem.ByteArrayT,
   754  			stackitem.ByteArrayT,
   755  			stackitem.IntegerT, // leave initial type
   756  			stackitem.ByteArrayT,
   757  		}, false)
   758  	})
   759  	t.Run("with ellipsis, do not emit conversion code", func(t *testing.T) {
   760  		checkSingleType(t, methodWithEllipsis, smartcontract.IntegerType, stackitem.IntegerT)
   761  		checkSingleType(t, methodWithEllipsis, smartcontract.BoolType, stackitem.IntegerT)
   762  		checkSingleType(t, methodWithEllipsis, smartcontract.ByteArrayType, stackitem.IntegerT)
   763  	})
   764  	t.Run("no events check => no conversion code", func(t *testing.T) {
   765  		checkSingleType(t, methodWithoutEllipsis, smartcontract.PublicKeyType, stackitem.IntegerT, true)
   766  	})
   767  }