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 }