github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/cli/smartcontract/contract_test.go (about)

     1  package smartcontract_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/nspcc-dev/neo-go/cli/smartcontract"
    15  	"github.com/nspcc-dev/neo-go/internal/random"
    16  	"github.com/nspcc-dev/neo-go/internal/testcli"
    17  	"github.com/nspcc-dev/neo-go/internal/versionutil"
    18  	"github.com/nspcc-dev/neo-go/pkg/config"
    19  	"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
    20  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    21  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    22  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    23  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    24  	"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
    25  	"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
    26  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    27  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
    28  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    29  	"github.com/nspcc-dev/neo-go/pkg/util"
    30  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    31  	"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
    32  	"github.com/nspcc-dev/neo-go/pkg/wallet"
    33  	"github.com/stretchr/testify/require"
    34  	"gopkg.in/yaml.v3"
    35  )
    36  
    37  // Keep contract NEFs consistent between runs.
    38  const _ = versionutil.TestVersion
    39  
    40  func TestCalcHash(t *testing.T) {
    41  	tmpDir := t.TempDir()
    42  	e := testcli.NewExecutor(t, false)
    43  
    44  	nefPath := "./testdata/verify.nef"
    45  	src, err := os.ReadFile(nefPath)
    46  	require.NoError(t, err)
    47  	nefF, err := nef.FileFromBytes(src)
    48  	require.NoError(t, err)
    49  	manifestPath := "./testdata/verify.manifest.json"
    50  	manifestBytes, err := os.ReadFile(manifestPath)
    51  	require.NoError(t, err)
    52  	manif := &manifest.Manifest{}
    53  	err = json.Unmarshal(manifestBytes, manif)
    54  	require.NoError(t, err)
    55  	sender := random.Uint160()
    56  
    57  	cmd := []string{"neo-go", "contract", "calc-hash"}
    58  	t.Run("no sender", func(t *testing.T) {
    59  		e.RunWithError(t, append(cmd, "--in", nefPath, "--manifest", manifestPath)...)
    60  	})
    61  	t.Run("no nef file", func(t *testing.T) {
    62  		e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...)
    63  	})
    64  	t.Run("no manifest file", func(t *testing.T) {
    65  		e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...)
    66  	})
    67  	t.Run("invalid nef path", func(t *testing.T) {
    68  		e.RunWithError(t, append(cmd, "--sender", sender.StringLE(),
    69  			"--in", "./testdata/verify.nef123", "--manifest", manifestPath)...)
    70  	})
    71  	t.Run("invalid manifest path", func(t *testing.T) {
    72  		e.RunWithError(t, append(cmd, "--sender", sender.StringLE(),
    73  			"--in", nefPath, "--manifest", "./testdata/verify.manifest123")...)
    74  	})
    75  	t.Run("invalid nef file", func(t *testing.T) {
    76  		p := filepath.Join(tmpDir, "neogo.calchash.verify.nef")
    77  		require.NoError(t, os.WriteFile(p, src[:4], os.ModePerm))
    78  		e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", p, "--manifest", manifestPath)...)
    79  	})
    80  	t.Run("invalid manifest file", func(t *testing.T) {
    81  		p := filepath.Join(tmpDir, "neogo.calchash.verify.manifest.json")
    82  		require.NoError(t, os.WriteFile(p, manifestBytes[:4], os.ModePerm))
    83  		e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", nefPath, "--manifest", p)...)
    84  	})
    85  
    86  	cmd = append(cmd, "--in", nefPath, "--manifest", manifestPath)
    87  	expected := state.CreateContractHash(sender, nefF.Checksum, manif.Name)
    88  	t.Run("excessive parameters", func(t *testing.T) {
    89  		e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "something")...)
    90  	})
    91  	t.Run("valid, uint160", func(t *testing.T) {
    92  		e.Run(t, append(cmd, "--sender", sender.StringLE())...)
    93  		e.CheckNextLine(t, expected.StringLE())
    94  	})
    95  	t.Run("valid, uint160 with 0x", func(t *testing.T) {
    96  		e.Run(t, append(cmd, "--sender", "0x"+sender.StringLE())...)
    97  		e.CheckNextLine(t, expected.StringLE())
    98  	})
    99  	t.Run("valid, address", func(t *testing.T) {
   100  		e.Run(t, append(cmd, "--sender", address.Uint160ToString(sender))...)
   101  		e.CheckNextLine(t, expected.StringLE())
   102  	})
   103  }
   104  
   105  func TestContractBindings(t *testing.T) {
   106  	// For proper contract init. The actual version as it will be replaced.
   107  	smartcontract.ModVersion = "v0.0.0"
   108  
   109  	tmpDir := t.TempDir()
   110  	e := testcli.NewExecutor(t, false)
   111  
   112  	ctrPath := filepath.Join(tmpDir, "testcontract")
   113  	e.Run(t, "neo-go", "contract", "init", "--name", ctrPath)
   114  
   115  	srcPath := filepath.Join(ctrPath, "main.go")
   116  	require.NoError(t, os.WriteFile(srcPath, []byte(`package testcontract
   117  import(
   118  	alias "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
   119  )
   120  type MyPair struct {
   121  	Key int
   122  	Value string
   123  }
   124  func ToMap(a []MyPair) map[int]string {
   125  	return nil
   126  }
   127  func ToArray(m map[int]string) []MyPair {
   128  	return nil
   129  }
   130  func Block() *alias.Block{
   131  	return alias.GetBlock(1)
   132  }
   133  func Blocks() []*alias.Block {
   134  	return []*alias.Block{
   135  		alias.GetBlock(10),
   136  		alias.GetBlock(11),
   137  	}
   138  }
   139  `), os.ModePerm))
   140  
   141  	cfgPath := filepath.Join(ctrPath, "neo-go.yml")
   142  	manifestPath := filepath.Join(tmpDir, "manifest.json")
   143  	bindingsPath := filepath.Join(tmpDir, "bindings.yml")
   144  	cmd := []string{"neo-go", "contract", "compile"}
   145  
   146  	cmd = append(cmd, "--in", ctrPath, "--bindings", bindingsPath)
   147  
   148  	// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
   149  	require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
   150  
   151  	cmd = append(cmd, "--config", cfgPath,
   152  		"--out", filepath.Join(tmpDir, "out.nef"),
   153  		"--manifest", manifestPath,
   154  		"--bindings", bindingsPath)
   155  	t.Run("excessive parameters", func(t *testing.T) {
   156  		e.RunWithError(t, append(cmd, "something")...)
   157  	})
   158  	e.Run(t, cmd...)
   159  	e.CheckEOF(t)
   160  	require.FileExists(t, bindingsPath)
   161  
   162  	outPath := filepath.Join(t.TempDir(), "binding.go")
   163  	e.Run(t, "neo-go", "contract", "generate-wrapper",
   164  		"--config", bindingsPath, "--manifest", manifestPath,
   165  		"--out", outPath, "--hash", "0x0123456789987654321001234567899876543210")
   166  
   167  	bs, err := os.ReadFile(outPath)
   168  	require.NoError(t, err)
   169  	require.Equal(t, `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
   170  
   171  // Package testcontract contains wrappers for testcontract contract.
   172  package testcontract
   173  
   174  import (
   175  	"github.com/nspcc-dev/neo-go/pkg/interop/contract"
   176  	"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
   177  	"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
   178  	"myimport.com/testcontract"
   179  )
   180  
   181  // Hash contains contract hash in big-endian form.
   182  const Hash = "\x10\x32\x54\x76\x98\x89\x67\x45\x23\x01\x10\x32\x54\x76\x98\x89\x67\x45\x23\x01"
   183  
   184  // Block invokes `+"`block`"+` method of contract.
   185  func Block() *ledger.Block {
   186  	return neogointernal.CallWithToken(Hash, "block", int(contract.All)).(*ledger.Block)
   187  }
   188  
   189  // Blocks invokes `+"`blocks`"+` method of contract.
   190  func Blocks() []*ledger.Block {
   191  	return neogointernal.CallWithToken(Hash, "blocks", int(contract.All)).([]*ledger.Block)
   192  }
   193  
   194  // ToArray invokes `+"`toArray`"+` method of contract.
   195  func ToArray(m map[int]string) []testcontract.MyPair {
   196  	return neogointernal.CallWithToken(Hash, "toArray", int(contract.All), m).([]testcontract.MyPair)
   197  }
   198  
   199  // ToMap invokes `+"`toMap`"+` method of contract.
   200  func ToMap(a []testcontract.MyPair) map[int]string {
   201  	return neogointernal.CallWithToken(Hash, "toMap", int(contract.All), a).(map[int]string)
   202  }
   203  `, string(bs))
   204  }
   205  
   206  // updateGoMod updates the go.mod file located in the specified directory.
   207  // It sets the module name and replaces the neo-go interop package path with
   208  // the provided one to avoid getting an actual module version.
   209  func updateGoMod(dir, moduleName, neoGoPath string) error {
   210  	goModPath := filepath.Join(dir, "go.mod")
   211  	data, err := os.ReadFile(goModPath)
   212  	if err != nil {
   213  		return fmt.Errorf("failed to read go.mod: %w", err)
   214  	}
   215  
   216  	i := bytes.IndexByte(data, '\n')
   217  	if i == -1 {
   218  		return fmt.Errorf("unexpected go.mod format")
   219  	}
   220  
   221  	updatedData := append([]byte("module "+moduleName), data[i:]...)
   222  	wd, err := os.Getwd()
   223  	if err != nil {
   224  		return fmt.Errorf("failed to get working directory: %w", err)
   225  	}
   226  
   227  	replacementPath := filepath.Join(wd, neoGoPath)
   228  	updatedData = append(updatedData, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "+replacementPath+" \n"...)
   229  
   230  	if err := os.WriteFile(goModPath, updatedData, os.ModePerm); err != nil {
   231  		return fmt.Errorf("failed to write updated go.mod: %w", err)
   232  	}
   233  
   234  	return nil
   235  }
   236  
   237  func TestDynamicWrapper(t *testing.T) {
   238  	// For proper contract init. The actual version as it will be replaced.
   239  	smartcontract.ModVersion = "v0.0.0"
   240  
   241  	tmpDir := t.TempDir()
   242  	e := testcli.NewExecutor(t, true)
   243  
   244  	ctrPath := "../smartcontract/testdata"
   245  
   246  	verifyHash := testcli.DeployContract(t, e, filepath.Join(ctrPath, "verify.go"), filepath.Join(ctrPath, "verify.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
   247  
   248  	helperContract := `package testcontract
   249  
   250  import (
   251  	"github.com/nspcc-dev/neo-go/pkg/interop"
   252  	verify "myimport.com/testcontract/bindings"
   253  )
   254  
   255  func CallVerifyContract(h interop.Hash160) bool{
   256  	contractInstance := verify.NewContract(h)
   257  	return contractInstance.Verify()
   258  }`
   259  
   260  	helperDir := filepath.Join(tmpDir, "helper")
   261  	e.Run(t, "neo-go", "contract", "init", "--name", helperDir)
   262  
   263  	require.NoError(t, updateGoMod(helperDir, "myimport.com/testcontract", "../../pkg/interop"))
   264  	require.NoError(t, os.WriteFile(filepath.Join(helperDir, "main.go"), []byte(helperContract), os.ModePerm))
   265  	require.NoError(t, os.Mkdir(filepath.Join(helperDir, "bindings"), os.ModePerm))
   266  
   267  	e.Run(t, "neo-go", "contract", "generate-wrapper",
   268  		"--config", filepath.Join(ctrPath, "verify.bindings.yml"), "--manifest", filepath.Join(ctrPath, "verify.manifest.json"),
   269  		"--out", filepath.Join(helperDir, "bindings", "testdata.go"))
   270  	e.Run(t, "neo-go", "contract", "compile", "--in", filepath.Join(helperDir, "main.go"), "--config", filepath.Join(helperDir, "neo-go.yml"))
   271  	helperHash := testcli.DeployContract(t, e, filepath.Join(helperDir, "main.go"), filepath.Join(helperDir, "neo-go.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
   272  
   273  	e.In.WriteString("one\r")
   274  	e.Run(t, "neo-go", "contract", "invokefunction",
   275  		"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   276  		"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, "--force", "--await", helperHash.StringLE(), "callVerifyContract", verifyHash.StringLE())
   277  
   278  	tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ")
   279  	aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
   280  	require.NoError(t, err)
   281  	require.Equal(t, aer[0].Stack[0].Value().(bool), true)
   282  }
   283  
   284  func TestContractInitAndCompile(t *testing.T) {
   285  	// For proper contract init. The actual version as it will be replaced.
   286  	smartcontract.ModVersion = "v0.0.0"
   287  
   288  	tmpDir := t.TempDir()
   289  	e := testcli.NewExecutor(t, false)
   290  
   291  	t.Run("no path is provided", func(t *testing.T) {
   292  		e.RunWithError(t, "neo-go", "contract", "init")
   293  	})
   294  	t.Run("invalid path", func(t *testing.T) {
   295  		e.RunWithError(t, "neo-go", "contract", "init", "--name", "\x00")
   296  	})
   297  
   298  	ctrPath := filepath.Join(tmpDir, "testcontract")
   299  	t.Run("excessive parameters", func(t *testing.T) {
   300  		e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath, "something")
   301  	})
   302  
   303  	e.Run(t, "neo-go", "contract", "init", "--name", ctrPath)
   304  
   305  	t.Run("don't rewrite existing directory", func(t *testing.T) {
   306  		e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath)
   307  	})
   308  
   309  	ctrRootPath := filepath.Join(ctrPath, "main")
   310  	srcPath := ctrRootPath + ".go"
   311  	cfgPath := filepath.Join(ctrPath, "neo-go.yml")
   312  	nefPath := filepath.Join(tmpDir, "testcontract.nef")
   313  	manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json")
   314  	cmd := []string{"neo-go", "contract", "compile"}
   315  	t.Run("missing source", func(t *testing.T) {
   316  		e.RunWithError(t, cmd...)
   317  	})
   318  
   319  	cmd = append(cmd, "--in", srcPath, "--out", nefPath, "--manifest", manifestPath)
   320  	t.Run("missing config, but require manifest", func(t *testing.T) {
   321  		e.RunWithError(t, cmd...)
   322  	})
   323  	t.Run("provided non-existent config", func(t *testing.T) {
   324  		cfgName := filepath.Join(ctrPath, "notexists.yml")
   325  		e.RunWithError(t, append(cmd, "--config", cfgName)...)
   326  	})
   327  	t.Run("provided corrupted config", func(t *testing.T) {
   328  		data, err := os.ReadFile(cfgPath)
   329  		require.NoError(t, err)
   330  		badCfg := filepath.Join(tmpDir, "bad.yml")
   331  		require.NoError(t, os.WriteFile(badCfg, data[:len(data)-5], os.ModePerm))
   332  		e.RunWithError(t, append(cmd, "--config", badCfg)...)
   333  	})
   334  
   335  	// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
   336  	require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
   337  
   338  	cmd = append(cmd, "--config", cfgPath)
   339  
   340  	t.Run("excessive parameters", func(t *testing.T) {
   341  		e.RunWithError(t, append(cmd, "something")...)
   342  	})
   343  
   344  	e.Run(t, cmd...)
   345  	e.CheckEOF(t)
   346  	require.FileExists(t, nefPath)
   347  	require.FileExists(t, manifestPath)
   348  
   349  	t.Run("output hex script with --verbose", func(t *testing.T) {
   350  		e.Run(t, append(cmd, "--verbose")...)
   351  		e.CheckNextLine(t, "^[0-9a-hA-H]+$")
   352  	})
   353  
   354  	t.Run("autocomplete outputs", func(t *testing.T) {
   355  		cfg, err := os.ReadFile(cfgPath)
   356  		require.NoError(t, err)
   357  		require.NoError(t, os.WriteFile(filepath.Join(ctrPath, "main.yml"), cfg, os.ModePerm))
   358  		e.Run(t, "neo-go", "contract", "compile", "--in", srcPath)
   359  		defaultNefPath := ctrRootPath + ".nef"
   360  		defaultManifestPath := ctrRootPath + ".manifest.json"
   361  		defaultBindingsPath := ctrRootPath + ".bindings.yml"
   362  		require.FileExists(t, defaultNefPath)
   363  		require.FileExists(t, defaultManifestPath)
   364  		require.FileExists(t, defaultBindingsPath)
   365  	})
   366  }
   367  
   368  // Checks that error is returned if GAS available for test-invoke exceeds
   369  // GAS needed to be consumed.
   370  func TestDeployBigContract(t *testing.T) {
   371  	e := testcli.NewExecutorWithConfig(t, true, true, func(c *config.Config) {
   372  		c.ApplicationConfiguration.RPC.MaxGasInvoke = fixedn.Fixed8(1)
   373  	})
   374  	tmpDir := t.TempDir()
   375  
   376  	nefName := filepath.Join(tmpDir, "deploy.nef")
   377  	manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
   378  	e.Run(t, "neo-go", "contract", "compile",
   379  		"--in", "testdata/deploy/main.go", // compile single file
   380  		"--config", "testdata/deploy/neo-go.yml",
   381  		"--out", nefName, "--manifest", manifestName)
   382  
   383  	e.In.WriteString(testcli.ValidatorPass + "\r")
   384  	e.RunWithError(t, "neo-go", "contract", "deploy",
   385  		"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   386  		"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   387  		"--in", nefName, "--manifest", manifestName)
   388  }
   389  
   390  func TestContractDeployWithData(t *testing.T) {
   391  	eCompile := testcli.NewExecutor(t, false)
   392  	tmpDir := t.TempDir()
   393  
   394  	nefName := filepath.Join(tmpDir, "deploy.nef")
   395  	manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
   396  	eCompile.Run(t, "neo-go", "contract", "compile",
   397  		"--in", "testdata/deploy/main.go", // compile single file
   398  		"--config", "testdata/deploy/neo-go.yml",
   399  		"--out", nefName, "--manifest", manifestName)
   400  
   401  	deployContract := func(t *testing.T, haveData bool, scope string, await bool) {
   402  		e := testcli.NewExecutor(t, true)
   403  		cmd := []string{
   404  			"neo-go", "contract", "deploy",
   405  			"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
   406  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   407  			"--in", nefName, "--manifest", manifestName,
   408  			"--force",
   409  		}
   410  
   411  		if await {
   412  			cmd = append(cmd, "--await")
   413  		}
   414  		if haveData {
   415  			cmd = append(cmd, "[", "key1", "12", "key2", "take_me_to_church", "]")
   416  		}
   417  		if scope != "" {
   418  			cmd = append(cmd, "--", testcli.ValidatorAddr+":"+scope)
   419  		} else {
   420  			scope = "CalledByEntry"
   421  		}
   422  		e.In.WriteString(testcli.ValidatorPass + "\r")
   423  		e.Run(t, cmd...)
   424  		var tx *transaction.Transaction
   425  		if await {
   426  			tx, _ = e.CheckAwaitableTxPersisted(t)
   427  		} else {
   428  			tx, _ = e.CheckTxPersisted(t)
   429  		}
   430  
   431  		require.Equal(t, scope, tx.Signers[0].Scopes.String())
   432  		if !haveData {
   433  			return
   434  		}
   435  
   436  		line, err := e.Out.ReadString('\n')
   437  		require.NoError(t, err)
   438  		line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
   439  		h, err := util.Uint160DecodeStringLE(line)
   440  		require.NoError(t, err)
   441  
   442  		e.Run(t, "neo-go", "contract", "testinvokefunction",
   443  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   444  			h.StringLE(),
   445  			"getValueWithKey", "key1",
   446  		)
   447  
   448  		res := new(result.Invoke)
   449  		require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
   450  		require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
   451  		require.Len(t, res.Stack, 1)
   452  		require.Equal(t, []byte{12}, res.Stack[0].Value())
   453  
   454  		e.Run(t, "neo-go", "contract", "testinvokefunction",
   455  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   456  			h.StringLE(),
   457  			"getValueWithKey", "key2",
   458  		)
   459  
   460  		res = new(result.Invoke)
   461  		require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
   462  		require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
   463  		require.Len(t, res.Stack, 1)
   464  		require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
   465  	}
   466  
   467  	deployContract(t, true, "", false)
   468  	deployContract(t, false, "Global", false)
   469  	deployContract(t, true, "Global", false)
   470  	deployContract(t, false, "", true)
   471  	deployContract(t, true, "Global", true)
   472  	deployContract(t, true, "", true)
   473  }
   474  
   475  func TestDeployWithSigners(t *testing.T) {
   476  	e := testcli.NewExecutor(t, true)
   477  	tmpDir := t.TempDir()
   478  
   479  	nefName := filepath.Join(tmpDir, "deploy.nef")
   480  	manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
   481  	e.Run(t, "neo-go", "contract", "compile",
   482  		"--in", "testdata/deploy/main.go",
   483  		"--config", "testdata/deploy/neo-go.yml",
   484  		"--out", nefName, "--manifest", manifestName)
   485  
   486  	t.Run("missing nef", func(t *testing.T) {
   487  		e.RunWithError(t, "neo-go", "contract", "deploy",
   488  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   489  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   490  			"--in", "", "--manifest", manifestName)
   491  	})
   492  	t.Run("missing manifest", func(t *testing.T) {
   493  		e.RunWithError(t, "neo-go", "contract", "deploy",
   494  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   495  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   496  			"--in", nefName, "--manifest", "")
   497  	})
   498  	t.Run("corrupted data", func(t *testing.T) {
   499  		e.RunWithError(t, "neo-go", "contract", "deploy",
   500  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   501  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   502  			"--in", nefName, "--manifest", manifestName,
   503  			"[", "str1")
   504  	})
   505  	t.Run("invalid data", func(t *testing.T) {
   506  		e.RunWithError(t, "neo-go", "contract", "deploy",
   507  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   508  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   509  			"--in", nefName, "--manifest", manifestName,
   510  			"str1", "str2")
   511  	})
   512  	t.Run("missing wallet", func(t *testing.T) {
   513  		e.RunWithError(t, "neo-go", "contract", "deploy",
   514  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   515  			"--address", testcli.ValidatorAddr,
   516  			"--in", nefName, "--manifest", manifestName,
   517  			"[", "str1", "str2", "]")
   518  	})
   519  	t.Run("missing RPC", func(t *testing.T) {
   520  		e.RunWithError(t, "neo-go", "contract", "deploy",
   521  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   522  			"--in", nefName, "--manifest", manifestName,
   523  			"[", "str1", "str2", "]")
   524  	})
   525  	e.In.WriteString(testcli.ValidatorPass + "\r")
   526  	e.Run(t, "neo-go", "contract", "deploy",
   527  		"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   528  		"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   529  		"--in", nefName, "--manifest", manifestName,
   530  		"--force",
   531  		"--", testcli.ValidatorAddr+":Global")
   532  	tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ")
   533  	require.Equal(t, transaction.Global, tx.Signers[0].Scopes)
   534  }
   535  
   536  func TestContractManifestGroups(t *testing.T) {
   537  	e := testcli.NewExecutor(t, true)
   538  	tmpDir := t.TempDir()
   539  
   540  	_, err := wallet.NewWalletFromFile(testcli.TestWalletPath)
   541  	require.NoError(t, err)
   542  
   543  	nefName := filepath.Join(tmpDir, "deploy.nef")
   544  	manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
   545  	e.Run(t, "neo-go", "contract", "compile",
   546  		"--in", "testdata/deploy/main.go", // compile single file
   547  		"--config", "testdata/deploy/neo-go.yml",
   548  		"--out", nefName, "--manifest", manifestName)
   549  
   550  	t.Run("missing wallet", func(t *testing.T) {
   551  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group")
   552  	})
   553  	t.Run("invalid wallet", func(t *testing.T) {
   554  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
   555  			"--wallet", t.TempDir())
   556  	})
   557  	t.Run("invalid sender", func(t *testing.T) {
   558  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
   559  			"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
   560  			"--sender", "not-a-sender")
   561  	})
   562  	t.Run("invalid NEF file", func(t *testing.T) {
   563  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
   564  			"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
   565  			"--sender", testcli.TestWalletAccount, "--nef", tmpDir)
   566  	})
   567  	t.Run("corrupted NEF file", func(t *testing.T) {
   568  		f := filepath.Join(tmpDir, "invalid.nef")
   569  		require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
   570  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
   571  			"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
   572  			"--sender", testcli.TestWalletAccount, "--nef", f)
   573  	})
   574  	t.Run("invalid manifest file", func(t *testing.T) {
   575  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
   576  			"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
   577  			"--sender", testcli.TestWalletAccount, "--nef", nefName,
   578  			"--manifest", tmpDir)
   579  	})
   580  	t.Run("corrupted manifest file", func(t *testing.T) {
   581  		f := filepath.Join(tmpDir, "invalid.manifest.json")
   582  		require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
   583  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
   584  			"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
   585  			"--sender", testcli.TestWalletAccount, "--nef", nefName,
   586  			"--manifest", f)
   587  	})
   588  	t.Run("unknown account", func(t *testing.T) {
   589  		e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
   590  			"--wallet", testcli.TestWalletPath, "--address", util.Uint160{}.StringLE(),
   591  			"--sender", testcli.TestWalletAccount, "--nef", nefName,
   592  			"--manifest", manifestName)
   593  	})
   594  	cmd := []string{"neo-go", "contract", "manifest", "add-group",
   595  		"--nef", nefName, "--manifest", manifestName}
   596  
   597  	t.Run("excessive parameters", func(t *testing.T) {
   598  		e.RunWithError(t, append(cmd, "--wallet", testcli.TestWalletPath,
   599  			"--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount, "something")...)
   600  	})
   601  	e.In.WriteString("testpass\r")
   602  	e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath,
   603  		"--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount)...)
   604  
   605  	e.In.WriteString("testpass\r") // should override signature with the previous sender
   606  	e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath,
   607  		"--sender", testcli.ValidatorAddr, "--address", testcli.TestWalletAccount)...)
   608  
   609  	e.In.WriteString(testcli.ValidatorPass + "\r")
   610  	e.Run(t, "neo-go", "contract", "deploy",
   611  		"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   612  		"--in", nefName, "--manifest", manifestName,
   613  		"--force",
   614  		"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr)
   615  }
   616  
   617  func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 {
   618  	return testcli.DeployContract(t, e, "testdata/verify.go", "testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
   619  }
   620  
   621  func TestContract_TestInvokeScript(t *testing.T) {
   622  	e := testcli.NewExecutor(t, true)
   623  	tmpDir := t.TempDir()
   624  	badNef := filepath.Join(tmpDir, "invalid.nef")
   625  	goodNef := filepath.Join(tmpDir, "deploy.nef")
   626  	manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
   627  	e.Run(t, "neo-go", "contract", "compile",
   628  		"--in", "testdata/deploy/main.go", // compile single file
   629  		"--config", "testdata/deploy/neo-go.yml",
   630  		"--out", goodNef, "--manifest", manifestName)
   631  
   632  	t.Run("missing in", func(t *testing.T) {
   633  		e.RunWithError(t, "neo-go", "contract", "testinvokescript",
   634  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
   635  	})
   636  	t.Run("unexisting in", func(t *testing.T) {
   637  		e.RunWithError(t, "neo-go", "contract", "testinvokescript",
   638  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   639  			"--in", badNef)
   640  	})
   641  	t.Run("invalid nef", func(t *testing.T) {
   642  		require.NoError(t, os.WriteFile(badNef, []byte("qwer"), os.ModePerm))
   643  		e.RunWithError(t, "neo-go", "contract", "testinvokescript",
   644  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   645  			"--in", badNef)
   646  	})
   647  	t.Run("invalid signers", func(t *testing.T) {
   648  		e.RunWithError(t, "neo-go", "contract", "testinvokescript",
   649  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   650  			"--in", goodNef, "--", "not-a-valid-signer")
   651  	})
   652  	t.Run("no RPC endpoint", func(t *testing.T) {
   653  		e.RunWithError(t, "neo-go", "contract", "testinvokescript",
   654  			"--rpc-endpoint", "http://123456789",
   655  			"--in", goodNef)
   656  	})
   657  	t.Run("good", func(t *testing.T) {
   658  		e.Run(t, "neo-go", "contract", "testinvokescript",
   659  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   660  			"--in", goodNef)
   661  	})
   662  	t.Run("good with hashed signer", func(t *testing.T) {
   663  		e.Run(t, "neo-go", "contract", "testinvokescript",
   664  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   665  			"--in", goodNef, "--", util.Uint160{1, 2, 3}.StringLE())
   666  	})
   667  	t.Run("good with addressed signer", func(t *testing.T) {
   668  		e.Run(t, "neo-go", "contract", "testinvokescript",
   669  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   670  			"--in", goodNef, "--", address.Uint160ToString(util.Uint160{1, 2, 3}))
   671  	})
   672  	t.Run("historic, invalid", func(t *testing.T) {
   673  		e.RunWithError(t, "neo-go", "contract", "testinvokescript",
   674  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   675  			"--historic", "bad",
   676  			"--in", goodNef)
   677  	})
   678  	t.Run("historic, index", func(t *testing.T) {
   679  		e.Run(t, "neo-go", "contract", "testinvokescript",
   680  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   681  			"--historic", "0",
   682  			"--in", goodNef)
   683  	})
   684  	t.Run("historic, hash", func(t *testing.T) {
   685  		e.Run(t, "neo-go", "contract", "testinvokescript",
   686  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   687  			"--historic", e.Chain.GetHeaderHash(0).StringLE(),
   688  			"--in", goodNef)
   689  	})
   690  }
   691  
   692  func TestComlileAndInvokeFunction(t *testing.T) {
   693  	e := testcli.NewExecutor(t, true)
   694  	tmpDir := t.TempDir()
   695  
   696  	nefName := filepath.Join(tmpDir, "deploy.nef")
   697  	manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
   698  	e.Run(t, "neo-go", "contract", "compile",
   699  		"--in", "testdata/deploy/main.go", // compile single file
   700  		"--config", "testdata/deploy/neo-go.yml",
   701  		"--out", nefName, "--manifest", manifestName)
   702  
   703  	tmp := t.TempDir()
   704  	configPath := filepath.Join(tmp, "config.yaml")
   705  	cfg := config.Wallet{
   706  		Path:     testcli.ValidatorWallet,
   707  		Password: testcli.ValidatorPass,
   708  	}
   709  	yml, err := yaml.Marshal(cfg)
   710  	require.NoError(t, err)
   711  	require.NoError(t, os.WriteFile(configPath, yml, 0666))
   712  	e.Run(t, "neo-go", "contract", "deploy",
   713  		"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--force",
   714  		"--wallet-config", configPath, "--address", testcli.ValidatorAddr,
   715  		"--in", nefName, "--manifest", manifestName)
   716  
   717  	e.CheckTxPersisted(t, "Sent invocation transaction ")
   718  	line, err := e.Out.ReadString('\n')
   719  	require.NoError(t, err)
   720  	line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
   721  	h, err := util.Uint160DecodeStringLE(line)
   722  	require.NoError(t, err)
   723  
   724  	t.Run("check calc hash", func(t *testing.T) {
   725  		// missing sender
   726  		e.RunWithError(t, "neo-go", "contract", "calc-hash",
   727  			"--in", nefName,
   728  			"--manifest", manifestName)
   729  
   730  		e.Run(t, "neo-go", "contract", "calc-hash",
   731  			"--sender", testcli.ValidatorAddr, "--in", nefName,
   732  			"--manifest", manifestName)
   733  		e.CheckNextLine(t, h.StringLE())
   734  	})
   735  
   736  	cmd := []string{"neo-go", "contract", "testinvokefunction",
   737  		"--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
   738  	t.Run("missing hash", func(t *testing.T) {
   739  		e.RunWithError(t, cmd...)
   740  	})
   741  	t.Run("invalid hash", func(t *testing.T) {
   742  		e.RunWithError(t, append(cmd, "notahash")...)
   743  	})
   744  
   745  	cmd = append(cmd, h.StringLE())
   746  	t.Run("missing method", func(t *testing.T) {
   747  		e.RunWithError(t, cmd...)
   748  	})
   749  
   750  	cmd = append(cmd, "getValue")
   751  	t.Run("invalid params", func(t *testing.T) {
   752  		e.RunWithError(t, append(cmd, "[")...)
   753  	})
   754  	t.Run("invalid cosigner", func(t *testing.T) {
   755  		e.RunWithError(t, append(cmd, "--", "notahash")...)
   756  	})
   757  	t.Run("missing RPC address", func(t *testing.T) {
   758  		e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
   759  			h.StringLE(), "getValue")
   760  	})
   761  
   762  	e.Run(t, cmd...)
   763  
   764  	checkGetValueOut := func(str string) {
   765  		res := new(result.Invoke)
   766  		require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
   767  		require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
   768  		require.Len(t, res.Stack, 1)
   769  		require.Equal(t, []byte(str), res.Stack[0].Value())
   770  	}
   771  	checkGetValueOut("on create|sub create")
   772  
   773  	// deploy verification contract
   774  	hVerify := deployVerifyContract(t, e)
   775  
   776  	t.Run("real invoke", func(t *testing.T) {
   777  		cmd := []string{"neo-go", "contract", "invokefunction",
   778  			"--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
   779  		t.Run("missing wallet", func(t *testing.T) {
   780  			cmd := append(cmd, h.StringLE(), "getValue")
   781  			e.RunWithError(t, cmd...)
   782  		})
   783  		t.Run("non-existent wallet", func(t *testing.T) {
   784  			cmd := append(cmd, "--wallet", filepath.Join(tmpDir, "not.exists"),
   785  				h.StringLE(), "getValue")
   786  			e.RunWithError(t, cmd...)
   787  		})
   788  		t.Run("corrupted wallet", func(t *testing.T) {
   789  			tmp := t.TempDir()
   790  			tmpPath := filepath.Join(tmp, "wallet.json")
   791  			require.NoError(t, os.WriteFile(tmpPath, []byte("{"), os.ModePerm))
   792  
   793  			cmd := append(cmd, "--wallet", tmpPath,
   794  				h.StringLE(), "getValue")
   795  			e.RunWithError(t, cmd...)
   796  		})
   797  		t.Run("non-existent address", func(t *testing.T) {
   798  			cmd := append(cmd, "--wallet", testcli.ValidatorWallet,
   799  				"--address", random.Uint160().StringLE(),
   800  				h.StringLE(), "getValue")
   801  			e.RunWithError(t, cmd...)
   802  		})
   803  		t.Run("invalid password", func(t *testing.T) {
   804  			e.In.WriteString("invalid_password\r")
   805  			cmd := append(cmd, "--wallet", testcli.ValidatorWallet,
   806  				h.StringLE(), "getValue")
   807  			e.RunWithError(t, cmd...)
   808  		})
   809  		t.Run("good: default address", func(t *testing.T) {
   810  			e.In.WriteString("one\r")
   811  			e.In.WriteString("y\r")
   812  			e.Run(t, append(cmd, "--wallet", testcli.ValidatorWallet, h.StringLE(), "getValue")...)
   813  		})
   814  		t.Run("good: from wallet config", func(t *testing.T) {
   815  			e.In.WriteString("y\r")
   816  			e.Run(t, append(cmd, "--wallet-config", configPath, h.StringLE(), "getValue")...)
   817  		})
   818  
   819  		cmd = append(cmd, "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr)
   820  		t.Run("cancelled", func(t *testing.T) {
   821  			e.In.WriteString("one\r")
   822  			e.In.WriteString("n\r")
   823  			e.RunWithError(t, append(cmd, h.StringLE(), "getValue")...)
   824  		})
   825  		t.Run("confirmed", func(t *testing.T) {
   826  			e.In.WriteString("one\r")
   827  			e.In.WriteString("y\r")
   828  			e.Run(t, append(cmd, h.StringLE(), "getValue")...)
   829  		})
   830  
   831  		t.Run("failind method", func(t *testing.T) {
   832  			e.In.WriteString("one\r")
   833  			e.In.WriteString("y\r")
   834  			e.RunWithError(t, append(cmd, h.StringLE(), "fail")...)
   835  
   836  			e.In.WriteString("one\r")
   837  			e.Run(t, append(cmd, "--force", h.StringLE(), "fail")...)
   838  		})
   839  
   840  		t.Run("cosigner is deployed contract", func(t *testing.T) {
   841  			e.In.WriteString("one\r")
   842  			e.In.WriteString("y\r")
   843  			e.Run(t, append(cmd, h.StringLE(), "getValue",
   844  				"--", testcli.ValidatorAddr, hVerify.StringLE())...)
   845  		})
   846  
   847  		t.Run("with await", func(t *testing.T) {
   848  			e.In.WriteString("one\r")
   849  			e.Run(t, append(cmd, "--force", "--await", h.StringLE(), "getValue")...)
   850  			e.CheckAwaitableTxPersisted(t)
   851  		})
   852  	})
   853  
   854  	t.Run("real invoke and save tx", func(t *testing.T) {
   855  		txout := filepath.Join(tmpDir, "test_contract_tx.json")
   856  
   857  		cmd = []string{"neo-go", "contract", "invokefunction",
   858  			"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
   859  			"--out", txout,
   860  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   861  		}
   862  
   863  		t.Run("without cosigner", func(t *testing.T) {
   864  			e.In.WriteString("one\r")
   865  			e.Run(t, append(cmd, hVerify.StringLE(), "verify")...)
   866  		})
   867  
   868  		t.Run("with cosigner", func(t *testing.T) {
   869  			t.Run("cosigner is sender (none)", func(t *testing.T) {
   870  				e.In.WriteString("one\r")
   871  				e.RunWithError(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":None")...)
   872  			})
   873  			t.Run("cosigner is sender (customcontract)", func(t *testing.T) {
   874  				e.In.WriteString("one\r")
   875  				e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":CustomContracts:"+h.StringLE())...)
   876  			})
   877  			t.Run("cosigner is sender (global)", func(t *testing.T) {
   878  				e.In.WriteString("one\r")
   879  				e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":Global")...)
   880  			})
   881  
   882  			acc, err := wallet.NewAccount()
   883  			require.NoError(t, err)
   884  			pk, err := keys.NewPrivateKey()
   885  			require.NoError(t, err)
   886  			err = acc.ConvertMultisig(2, keys.PublicKeys{acc.PublicKey(), pk.PublicKey()})
   887  			require.NoError(t, err)
   888  
   889  			t.Run("cosigner is multisig account", func(t *testing.T) {
   890  				t.Run("missing in the wallet", func(t *testing.T) {
   891  					e.In.WriteString("one\r")
   892  					e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", acc.Address)...)
   893  				})
   894  
   895  				t.Run("good", func(t *testing.T) {
   896  					e.In.WriteString("one\r")
   897  					e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", testcli.MultisigAddr)...)
   898  				})
   899  			})
   900  
   901  			t.Run("cosigner is deployed contract", func(t *testing.T) {
   902  				t.Run("missing in the wallet", func(t *testing.T) {
   903  					e.In.WriteString("one\r")
   904  					e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", h.StringLE())...)
   905  				})
   906  
   907  				t.Run("good", func(t *testing.T) {
   908  					e.In.WriteString("one\r")
   909  					e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", hVerify.StringLE())...)
   910  				})
   911  			})
   912  		})
   913  	})
   914  
   915  	t.Run("test Storage.Find", func(t *testing.T) {
   916  		cmd := []string{"neo-go", "contract", "testinvokefunction",
   917  			"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
   918  			h.StringLE(), "testFind"}
   919  
   920  		t.Run("keys only", func(t *testing.T) {
   921  			e.Run(t, append(cmd, strconv.FormatInt(storage.FindKeysOnly, 10))...)
   922  			res := new(result.Invoke)
   923  			require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
   924  			require.Equal(t, vmstate.Halt.String(), res.State)
   925  			require.Len(t, res.Stack, 1)
   926  			require.Equal(t, []stackitem.Item{
   927  				stackitem.Make("findkey1"),
   928  				stackitem.Make("findkey2"),
   929  			}, res.Stack[0].Value())
   930  		})
   931  		t.Run("both", func(t *testing.T) {
   932  			e.Run(t, append(cmd, strconv.FormatInt(storage.FindDefault, 10))...)
   933  			res := new(result.Invoke)
   934  			require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
   935  			require.Equal(t, vmstate.Halt.String(), res.State)
   936  			require.Len(t, res.Stack, 1)
   937  
   938  			arr, ok := res.Stack[0].Value().([]stackitem.Item)
   939  			require.True(t, ok)
   940  			require.Len(t, arr, 2)
   941  			require.Equal(t, []stackitem.Item{
   942  				stackitem.Make("findkey1"), stackitem.Make("value1"),
   943  			}, arr[0].Value())
   944  			require.Equal(t, []stackitem.Item{
   945  				stackitem.Make("findkey2"), stackitem.Make("value2"),
   946  			}, arr[1].Value())
   947  		})
   948  	})
   949  
   950  	var (
   951  		hashBeforeUpdate  util.Uint256
   952  		indexBeforeUpdate uint32
   953  		indexAfterUpdate  uint32
   954  		stateBeforeUpdate util.Uint256
   955  	)
   956  	t.Run("Update", func(t *testing.T) {
   957  		nefName := filepath.Join(tmpDir, "updated.nef")
   958  		manifestName := filepath.Join(tmpDir, "updated.manifest.json")
   959  		e.Run(t, "neo-go", "contract", "compile",
   960  			"--config", "testdata/deploy/neo-go.yml",
   961  			"--in", "testdata/deploy/", // compile all files in dir
   962  			"--out", nefName, "--manifest", manifestName)
   963  
   964  		t.Cleanup(func() {
   965  			os.Remove(nefName)
   966  			os.Remove(manifestName)
   967  		})
   968  
   969  		rawNef, err := os.ReadFile(nefName)
   970  		require.NoError(t, err)
   971  		rawManifest, err := os.ReadFile(manifestName)
   972  		require.NoError(t, err)
   973  
   974  		indexBeforeUpdate = e.Chain.BlockHeight()
   975  		hashBeforeUpdate = e.Chain.CurrentHeaderHash()
   976  		mptBeforeUpdate, err := e.Chain.GetStateRoot(indexBeforeUpdate)
   977  		require.NoError(t, err)
   978  		stateBeforeUpdate = mptBeforeUpdate.Root
   979  		e.In.WriteString("one\r")
   980  		e.Run(t, "neo-go", "contract", "invokefunction",
   981  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   982  			"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
   983  			"--force",
   984  			h.StringLE(), "update",
   985  			"bytes:"+hex.EncodeToString(rawNef),
   986  			"bytes:"+hex.EncodeToString(rawManifest),
   987  		)
   988  		e.CheckTxPersisted(t, "Sent invocation transaction ")
   989  
   990  		indexAfterUpdate = e.Chain.BlockHeight()
   991  		e.In.WriteString("one\r")
   992  		e.Run(t, "neo-go", "contract", "testinvokefunction",
   993  			"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
   994  			h.StringLE(), "getValue")
   995  		checkGetValueOut("on update|sub update")
   996  	})
   997  	t.Run("historic", func(t *testing.T) {
   998  		t.Run("bad ref", func(t *testing.T) {
   999  			e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
  1000  				"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
  1001  				"--historic", "bad",
  1002  				h.StringLE(), "getValue")
  1003  		})
  1004  		for name, ref := range map[string]string{
  1005  			"by index":      strconv.FormatUint(uint64(indexBeforeUpdate), 10),
  1006  			"by block hash": hashBeforeUpdate.StringLE(),
  1007  			"by state hash": stateBeforeUpdate.StringLE(),
  1008  		} {
  1009  			t.Run(name, func(t *testing.T) {
  1010  				e.Run(t, "neo-go", "contract", "testinvokefunction",
  1011  					"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
  1012  					"--historic", ref,
  1013  					h.StringLE(), "getValue")
  1014  			})
  1015  			checkGetValueOut("on create|sub create")
  1016  		}
  1017  		t.Run("updated historic", func(t *testing.T) {
  1018  			e.Run(t, "neo-go", "contract", "testinvokefunction",
  1019  				"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
  1020  				"--historic", strconv.FormatUint(uint64(indexAfterUpdate), 10),
  1021  				h.StringLE(), "getValue")
  1022  			checkGetValueOut("on update|sub update")
  1023  		})
  1024  	})
  1025  }
  1026  
  1027  func TestContractInspect(t *testing.T) {
  1028  	e := testcli.NewExecutor(t, false)
  1029  	const srcPath = "testdata/deploy/main.go"
  1030  	tmpDir := t.TempDir()
  1031  
  1032  	nefName := filepath.Join(tmpDir, "deploy.nef")
  1033  	manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
  1034  	e.Run(t, "neo-go", "contract", "compile",
  1035  		"--in", srcPath,
  1036  		"--config", "testdata/deploy/neo-go.yml",
  1037  		"--out", nefName, "--manifest", manifestName)
  1038  
  1039  	cmd := []string{"neo-go", "contract", "inspect"}
  1040  	t.Run("missing input", func(t *testing.T) {
  1041  		e.RunWithError(t, cmd...)
  1042  	})
  1043  	t.Run("with raw '.go'", func(t *testing.T) {
  1044  		e.RunWithError(t, append(cmd, "--in", srcPath)...)
  1045  		e.Run(t, append(cmd, "--in", srcPath, "--compile")...)
  1046  		require.True(t, strings.Contains(e.Out.String(), "SYSCALL"))
  1047  	})
  1048  	t.Run("with nef", func(t *testing.T) {
  1049  		e.RunWithError(t, append(cmd, "--in", nefName, "--compile")...)
  1050  		e.RunWithError(t, append(cmd, "--in", filepath.Join(tmpDir, "not.exists"))...)
  1051  		e.RunWithError(t, append(cmd, "--in", nefName, "something")...)
  1052  		e.Run(t, append(cmd, "--in", nefName)...)
  1053  		require.True(t, strings.Contains(e.Out.String(), "SYSCALL"))
  1054  	})
  1055  }
  1056  
  1057  func TestCompileExamples(t *testing.T) {
  1058  	tmpDir := t.TempDir()
  1059  	const examplePath = "../../examples"
  1060  	infos, err := os.ReadDir(examplePath)
  1061  	require.NoError(t, err)
  1062  
  1063  	e := testcli.NewExecutor(t, false)
  1064  
  1065  	for _, info := range infos {
  1066  		if !info.IsDir() {
  1067  			// example smart contracts are located in the `/examples` subdirectories, but
  1068  			// there are also a couple of files inside the `/examples` which doesn't need to be compiled
  1069  			continue
  1070  		}
  1071  		if info.Name() == "zkp" {
  1072  			// A set of special ZKP-related examples, they have their own tests.
  1073  			continue
  1074  		}
  1075  		t.Run(info.Name(), func(t *testing.T) {
  1076  			infos, err := os.ReadDir(filepath.Join(examplePath, info.Name()))
  1077  			require.NoError(t, err)
  1078  			require.False(t, len(infos) == 0, "detected smart contract folder with no contract in it")
  1079  
  1080  			outF := filepath.Join(tmpDir, info.Name()+".nef")
  1081  			manifestF := filepath.Join(tmpDir, info.Name()+".manifest.json")
  1082  			bindingF := filepath.Join(tmpDir, info.Name()+".binding.yml")
  1083  			wrapperF := filepath.Join(tmpDir, info.Name()+".go")
  1084  			rpcWrapperF := filepath.Join(tmpDir, info.Name()+".rpc.go")
  1085  
  1086  			cfgName := filterFilename(infos, ".yml")
  1087  			opts := []string{
  1088  				"neo-go", "contract", "compile",
  1089  				"--in", filepath.Join(examplePath, info.Name()),
  1090  				"--out", outF,
  1091  				"--manifest", manifestF,
  1092  				"--config", filepath.Join(examplePath, info.Name(), cfgName),
  1093  				"--bindings", bindingF,
  1094  			}
  1095  			e.Run(t, opts...)
  1096  
  1097  			if info.Name() == "storage" {
  1098  				rawM, err := os.ReadFile(manifestF)
  1099  				require.NoError(t, err)
  1100  
  1101  				m := new(manifest.Manifest)
  1102  				require.NoError(t, json.Unmarshal(rawM, m))
  1103  
  1104  				require.Nil(t, m.ABI.GetMethod("getDefault", 0))
  1105  				require.NotNil(t, m.ABI.GetMethod("get", 0))
  1106  				require.NotNil(t, m.ABI.GetMethod("get", 1))
  1107  
  1108  				require.Nil(t, m.ABI.GetMethod("putDefault", 1))
  1109  				require.NotNil(t, m.ABI.GetMethod("put", 1))
  1110  				require.NotNil(t, m.ABI.GetMethod("put", 2))
  1111  			}
  1112  			e.Run(t, "neo-go", "contract", "generate-wrapper",
  1113  				"--manifest", manifestF,
  1114  				"--config", bindingF,
  1115  				"--out", wrapperF,
  1116  				"--hash", "0x00112233445566778899aabbccddeeff00112233")
  1117  			e.Run(t, "neo-go", "contract", "generate-rpcwrapper",
  1118  				"--manifest", manifestF,
  1119  				"--config", bindingF,
  1120  				"--out", rpcWrapperF,
  1121  				"--hash", "0x00112233445566778899aabbccddeeff00112233")
  1122  		})
  1123  	}
  1124  
  1125  	t.Run("invalid manifest", func(t *testing.T) {
  1126  		const dir = "./testdata/"
  1127  		for _, name := range []string{"invalid1", "invalid2", "invalid3", "invalid4"} {
  1128  			outF := filepath.Join(tmpDir, name+".nef")
  1129  			manifestF := filepath.Join(tmpDir, name+".manifest.json")
  1130  			e.RunWithError(t, "neo-go", "contract", "compile",
  1131  				"--in", filepath.Join(dir, name),
  1132  				"--out", outF,
  1133  				"--manifest", manifestF,
  1134  				"--config", filepath.Join(dir, name, "invalid.yml"),
  1135  			)
  1136  		}
  1137  	})
  1138  }
  1139  
  1140  func filterFilename(infos []os.DirEntry, ext string) string {
  1141  	for _, info := range infos {
  1142  		if !info.IsDir() {
  1143  			name := info.Name()
  1144  			if strings.HasSuffix(name, ext) {
  1145  				return name
  1146  			}
  1147  		}
  1148  	}
  1149  	return ""
  1150  }
  1151  
  1152  func TestContractCompile_NEFSizeCheck(t *testing.T) {
  1153  	tmpDir := t.TempDir()
  1154  	e := testcli.NewExecutor(t, false)
  1155  
  1156  	src := `package nefconstraints
  1157         var data = "%s"
  1158  
  1159         func Main() string {
  1160                 return data
  1161         }`
  1162  	data := make([]byte, stackitem.MaxSize-10)
  1163  	for i := range data {
  1164  		data[i] = byte('a')
  1165  	}
  1166  
  1167  	in := filepath.Join(tmpDir, "main.go")
  1168  	cfg := filepath.Join(tmpDir, "main.yml")
  1169  	require.NoError(t, os.WriteFile(cfg, []byte("name: main"), os.ModePerm))
  1170  	require.NoError(t, os.WriteFile(in, []byte(fmt.Sprintf(src, data)), os.ModePerm))
  1171  
  1172  	e.RunWithError(t, "neo-go", "contract", "compile", "--in", in)
  1173  	require.NoFileExists(t, filepath.Join(tmpDir, "main.nef"))
  1174  }