mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 13:41:37 +00:00
7b199af3b7
For passing address or hash AddressFlag is more suitable. Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
1183 lines
42 KiB
Go
1183 lines
42 KiB
Go
package smartcontract_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neo-go/cli/smartcontract"
|
|
"github.com/nspcc-dev/neo-go/internal/random"
|
|
"github.com/nspcc-dev/neo-go/internal/testcli"
|
|
"github.com/nspcc-dev/neo-go/internal/versionutil"
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
|
"github.com/stretchr/testify/require"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Keep contract NEFs consistent between runs.
|
|
const _ = versionutil.TestVersion
|
|
|
|
func TestCalcHash(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
e := testcli.NewExecutor(t, false)
|
|
|
|
nefPath := "./testdata/verify.nef"
|
|
src, err := os.ReadFile(nefPath)
|
|
require.NoError(t, err)
|
|
nefF, err := nef.FileFromBytes(src)
|
|
require.NoError(t, err)
|
|
manifestPath := "./testdata/verify.manifest.json"
|
|
manifestBytes, err := os.ReadFile(manifestPath)
|
|
require.NoError(t, err)
|
|
manif := &manifest.Manifest{}
|
|
err = json.Unmarshal(manifestBytes, manif)
|
|
require.NoError(t, err)
|
|
sender := random.Uint160()
|
|
|
|
cmd := []string{"neo-go", "contract", "calc-hash"}
|
|
t.Run("no sender", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "sender" not set`, append(cmd, "--in", nefPath, "--manifest", manifestPath)...)
|
|
})
|
|
t.Run("no nef file", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "in" not set`, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...)
|
|
})
|
|
t.Run("no manifest file", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "manifest" not set`, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...)
|
|
})
|
|
t.Run("invalid nef path", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(),
|
|
"--in", "./testdata/verify.nef123", "--manifest", manifestPath)...)
|
|
})
|
|
t.Run("invalid manifest path", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(),
|
|
"--in", nefPath, "--manifest", "./testdata/verify.manifest123")...)
|
|
})
|
|
t.Run("invalid nef file", func(t *testing.T) {
|
|
p := filepath.Join(tmpDir, "neogo.calchash.verify.nef")
|
|
require.NoError(t, os.WriteFile(p, src[:4], os.ModePerm))
|
|
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", p, "--manifest", manifestPath)...)
|
|
})
|
|
t.Run("invalid manifest file", func(t *testing.T) {
|
|
p := filepath.Join(tmpDir, "neogo.calchash.verify.manifest.json")
|
|
require.NoError(t, os.WriteFile(p, manifestBytes[:4], os.ModePerm))
|
|
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", nefPath, "--manifest", p)...)
|
|
})
|
|
|
|
cmd = append(cmd, "--in", nefPath, "--manifest", manifestPath)
|
|
expected := state.CreateContractHash(sender, nefF.Checksum, manif.Name)
|
|
t.Run("excessive parameters", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "something")...)
|
|
})
|
|
t.Run("valid, uint160", func(t *testing.T) {
|
|
e.Run(t, append(cmd, "--sender", sender.StringLE())...)
|
|
e.CheckNextLine(t, expected.StringLE())
|
|
})
|
|
t.Run("valid, uint160 with 0x", func(t *testing.T) {
|
|
e.Run(t, append(cmd, "--sender", "0x"+sender.StringLE())...)
|
|
e.CheckNextLine(t, expected.StringLE())
|
|
})
|
|
t.Run("valid, address", func(t *testing.T) {
|
|
e.Run(t, append(cmd, "--sender", address.Uint160ToString(sender))...)
|
|
e.CheckNextLine(t, expected.StringLE())
|
|
})
|
|
}
|
|
|
|
func TestContractBindings(t *testing.T) {
|
|
// For proper contract init. The actual version as it will be replaced.
|
|
smartcontract.ModVersion = "v0.0.0"
|
|
|
|
tmpDir := t.TempDir()
|
|
e := testcli.NewExecutor(t, false)
|
|
|
|
ctrPath := filepath.Join(tmpDir, "testcontract")
|
|
e.Run(t, "neo-go", "contract", "init", "--name", ctrPath)
|
|
|
|
srcPath := filepath.Join(ctrPath, "main.go")
|
|
require.NoError(t, os.WriteFile(srcPath, []byte(`package testcontract
|
|
import(
|
|
alias "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
|
)
|
|
type MyPair struct {
|
|
Key int
|
|
Value string
|
|
}
|
|
func ToMap(a []MyPair) map[int]string {
|
|
return nil
|
|
}
|
|
func ToArray(m map[int]string) []MyPair {
|
|
return nil
|
|
}
|
|
func Block() *alias.Block{
|
|
return alias.GetBlock(1)
|
|
}
|
|
func Blocks() []*alias.Block {
|
|
return []*alias.Block{
|
|
alias.GetBlock(10),
|
|
alias.GetBlock(11),
|
|
}
|
|
}
|
|
`), os.ModePerm))
|
|
|
|
cfgPath := filepath.Join(ctrPath, "neo-go.yml")
|
|
manifestPath := filepath.Join(tmpDir, "manifest.json")
|
|
bindingsPath := filepath.Join(tmpDir, "bindings.yml")
|
|
cmd := []string{"neo-go", "contract", "compile"}
|
|
|
|
cmd = append(cmd, "--in", ctrPath, "--bindings", bindingsPath)
|
|
|
|
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
|
|
require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
|
|
|
|
cmd = append(cmd, "--config", cfgPath,
|
|
"--out", filepath.Join(tmpDir, "out.nef"),
|
|
"--manifest", manifestPath,
|
|
"--bindings", bindingsPath)
|
|
t.Run("excessive parameters", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "something")...)
|
|
})
|
|
e.Run(t, cmd...)
|
|
e.CheckEOF(t)
|
|
require.FileExists(t, bindingsPath)
|
|
|
|
outPath := filepath.Join(t.TempDir(), "binding.go")
|
|
e.Run(t, "neo-go", "contract", "generate-wrapper",
|
|
"--config", bindingsPath, "--manifest", manifestPath,
|
|
"--out", outPath, "--hash", "0x0123456789987654321001234567899876543210")
|
|
|
|
bs, err := os.ReadFile(outPath)
|
|
require.NoError(t, err)
|
|
require.Equal(t, `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
|
|
|
|
// Package testcontract contains wrappers for testcontract contract.
|
|
package testcontract
|
|
|
|
import (
|
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
|
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
|
|
"myimport.com/testcontract"
|
|
)
|
|
|
|
// Hash contains contract hash in big-endian form.
|
|
const Hash = "\x10\x32\x54\x76\x98\x89\x67\x45\x23\x01\x10\x32\x54\x76\x98\x89\x67\x45\x23\x01"
|
|
|
|
// Block invokes `+"`block`"+` method of contract.
|
|
func Block() *ledger.Block {
|
|
return neogointernal.CallWithToken(Hash, "block", int(contract.All)).(*ledger.Block)
|
|
}
|
|
|
|
// Blocks invokes `+"`blocks`"+` method of contract.
|
|
func Blocks() []*ledger.Block {
|
|
return neogointernal.CallWithToken(Hash, "blocks", int(contract.All)).([]*ledger.Block)
|
|
}
|
|
|
|
// ToArray invokes `+"`toArray`"+` method of contract.
|
|
func ToArray(m map[int]string) []testcontract.MyPair {
|
|
return neogointernal.CallWithToken(Hash, "toArray", int(contract.All), m).([]testcontract.MyPair)
|
|
}
|
|
|
|
// ToMap invokes `+"`toMap`"+` method of contract.
|
|
func ToMap(a []testcontract.MyPair) map[int]string {
|
|
return neogointernal.CallWithToken(Hash, "toMap", int(contract.All), a).(map[int]string)
|
|
}
|
|
`, string(bs))
|
|
}
|
|
|
|
// updateGoMod updates the go.mod file located in the specified directory.
|
|
// It sets the module name and replaces the neo-go interop package path with
|
|
// the provided one to avoid getting an actual module version.
|
|
func updateGoMod(dir, moduleName, neoGoPath string) error {
|
|
goModPath := filepath.Join(dir, "go.mod")
|
|
data, err := os.ReadFile(goModPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read go.mod: %w", err)
|
|
}
|
|
|
|
i := bytes.IndexByte(data, '\n')
|
|
if i == -1 {
|
|
return fmt.Errorf("unexpected go.mod format")
|
|
}
|
|
|
|
updatedData := append([]byte("module "+moduleName), data[i:]...)
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get working directory: %w", err)
|
|
}
|
|
|
|
replacementPath := filepath.Join(wd, neoGoPath)
|
|
updatedData = append(updatedData, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "+replacementPath+" \n"...)
|
|
|
|
if err := os.WriteFile(goModPath, updatedData, os.ModePerm); err != nil {
|
|
return fmt.Errorf("failed to write updated go.mod: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func TestDynamicWrapper(t *testing.T) {
|
|
// For proper contract init. The actual version as it will be replaced.
|
|
smartcontract.ModVersion = "v0.0.0"
|
|
|
|
tmpDir := t.TempDir()
|
|
e := testcli.NewExecutor(t, true)
|
|
|
|
ctrPath := "../smartcontract/testdata"
|
|
|
|
verifyHash := testcli.DeployContract(t, e, filepath.Join(ctrPath, "verify.go"), filepath.Join(ctrPath, "verify.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
|
|
|
|
helperContract := `package testcontract
|
|
|
|
import (
|
|
"github.com/nspcc-dev/neo-go/pkg/interop"
|
|
verify "myimport.com/testcontract/bindings"
|
|
)
|
|
|
|
func CallVerifyContract(h interop.Hash160) bool{
|
|
contractInstance := verify.NewContract(h)
|
|
return contractInstance.Verify()
|
|
}`
|
|
|
|
helperDir := filepath.Join(tmpDir, "helper")
|
|
e.Run(t, "neo-go", "contract", "init", "--name", helperDir)
|
|
|
|
require.NoError(t, updateGoMod(helperDir, "myimport.com/testcontract", "../../pkg/interop"))
|
|
require.NoError(t, os.WriteFile(filepath.Join(helperDir, "main.go"), []byte(helperContract), os.ModePerm))
|
|
require.NoError(t, os.Mkdir(filepath.Join(helperDir, "bindings"), os.ModePerm))
|
|
|
|
e.Run(t, "neo-go", "contract", "generate-wrapper",
|
|
"--config", filepath.Join(ctrPath, "verify.bindings.yml"), "--manifest", filepath.Join(ctrPath, "verify.manifest.json"),
|
|
"--out", filepath.Join(helperDir, "bindings", "testdata.go"))
|
|
e.Run(t, "neo-go", "contract", "compile", "--in", filepath.Join(helperDir, "main.go"), "--config", filepath.Join(helperDir, "neo-go.yml"))
|
|
helperHash := testcli.DeployContract(t, e, filepath.Join(helperDir, "main.go"), filepath.Join(helperDir, "neo-go.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
|
|
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, "neo-go", "contract", "invokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, "--force", "--await", helperHash.StringLE(), "callVerifyContract", verifyHash.StringLE())
|
|
|
|
tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ")
|
|
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
|
require.NoError(t, err)
|
|
require.Equal(t, aer[0].Stack[0].Value().(bool), true)
|
|
}
|
|
|
|
func TestContractInitAndCompile(t *testing.T) {
|
|
// For proper contract init. The actual version as it will be replaced.
|
|
smartcontract.ModVersion = "v0.0.0"
|
|
|
|
tmpDir := t.TempDir()
|
|
e := testcli.NewExecutor(t, false)
|
|
|
|
t.Run("no path is provided", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "name" not set`, "neo-go", "contract", "init")
|
|
})
|
|
t.Run("invalid path", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "init", "--name", "\x00")
|
|
})
|
|
|
|
ctrPath := filepath.Join(tmpDir, "testcontract")
|
|
t.Run("excessive parameters", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath, "something")
|
|
})
|
|
|
|
e.Run(t, "neo-go", "contract", "init", "--name", ctrPath)
|
|
|
|
t.Run("don't rewrite existing directory", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath)
|
|
})
|
|
|
|
ctrRootPath := filepath.Join(ctrPath, "main")
|
|
srcPath := ctrRootPath + ".go"
|
|
cfgPath := filepath.Join(ctrPath, "neo-go.yml")
|
|
nefPath := filepath.Join(tmpDir, "testcontract.nef")
|
|
manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json")
|
|
cmd := []string{"neo-go", "contract", "compile"}
|
|
t.Run("missing source", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...)
|
|
})
|
|
|
|
cmd = append(cmd, "--in", srcPath, "--out", nefPath, "--manifest", manifestPath)
|
|
t.Run("missing config, but require manifest", func(t *testing.T) {
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
t.Run("provided non-existent config", func(t *testing.T) {
|
|
cfgName := filepath.Join(ctrPath, "notexists.yml")
|
|
e.RunWithError(t, append(cmd, "--config", cfgName)...)
|
|
})
|
|
t.Run("provided corrupted config", func(t *testing.T) {
|
|
data, err := os.ReadFile(cfgPath)
|
|
require.NoError(t, err)
|
|
badCfg := filepath.Join(tmpDir, "bad.yml")
|
|
require.NoError(t, os.WriteFile(badCfg, data[:len(data)-5], os.ModePerm))
|
|
e.RunWithError(t, append(cmd, "--config", badCfg)...)
|
|
})
|
|
|
|
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
|
|
require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
|
|
|
|
cmd = append(cmd, "--config", cfgPath)
|
|
|
|
t.Run("excessive parameters", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "something")...)
|
|
})
|
|
|
|
e.Run(t, cmd...)
|
|
e.CheckEOF(t)
|
|
require.FileExists(t, nefPath)
|
|
require.FileExists(t, manifestPath)
|
|
|
|
t.Run("output hex script with --verbose", func(t *testing.T) {
|
|
e.Run(t, append(cmd, "--verbose")...)
|
|
e.CheckNextLine(t, "^[0-9a-hA-H]+$")
|
|
})
|
|
|
|
t.Run("autocomplete outputs", func(t *testing.T) {
|
|
cfg, err := os.ReadFile(cfgPath)
|
|
require.NoError(t, err)
|
|
require.NoError(t, os.WriteFile(filepath.Join(ctrPath, "main.yml"), cfg, os.ModePerm))
|
|
e.Run(t, "neo-go", "contract", "compile", "--in", srcPath)
|
|
defaultNefPath := ctrRootPath + ".nef"
|
|
defaultManifestPath := ctrRootPath + ".manifest.json"
|
|
defaultBindingsPath := ctrRootPath + ".bindings.yml"
|
|
require.FileExists(t, defaultNefPath)
|
|
require.FileExists(t, defaultManifestPath)
|
|
require.FileExists(t, defaultBindingsPath)
|
|
})
|
|
}
|
|
|
|
// Checks that error is returned if GAS available for test-invoke exceeds
|
|
// GAS needed to be consumed.
|
|
func TestDeployBigContract(t *testing.T) {
|
|
e := testcli.NewExecutorWithConfig(t, true, true, func(c *config.Config) {
|
|
c.ApplicationConfiguration.RPC.MaxGasInvoke = fixedn.Fixed8(1)
|
|
})
|
|
tmpDir := t.TempDir()
|
|
|
|
nefName := filepath.Join(tmpDir, "deploy.nef")
|
|
manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
|
|
e.Run(t, "neo-go", "contract", "compile",
|
|
"--in", "testdata/deploy/main.go", // compile single file
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--out", nefName, "--manifest", manifestName)
|
|
|
|
e.In.WriteString(testcli.ValidatorPass + "\r")
|
|
e.RunWithError(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName)
|
|
}
|
|
|
|
func TestContractDeployWithData(t *testing.T) {
|
|
eCompile := testcli.NewExecutor(t, false)
|
|
tmpDir := t.TempDir()
|
|
|
|
nefName := filepath.Join(tmpDir, "deploy.nef")
|
|
manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
|
|
eCompile.Run(t, "neo-go", "contract", "compile",
|
|
"--in", "testdata/deploy/main.go", // compile single file
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--out", nefName, "--manifest", manifestName)
|
|
|
|
deployContract := func(t *testing.T, haveData bool, scope string, await bool) {
|
|
e := testcli.NewExecutor(t, true)
|
|
cmd := []string{
|
|
"neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName,
|
|
"--force",
|
|
}
|
|
|
|
if await {
|
|
cmd = append(cmd, "--await")
|
|
}
|
|
if haveData {
|
|
cmd = append(cmd, "[", "key1", "12", "key2", "take_me_to_church", "]")
|
|
}
|
|
if scope != "" {
|
|
cmd = append(cmd, "--", testcli.ValidatorAddr+":"+scope)
|
|
} else {
|
|
scope = "CalledByEntry"
|
|
}
|
|
e.In.WriteString(testcli.ValidatorPass + "\r")
|
|
e.Run(t, cmd...)
|
|
var tx *transaction.Transaction
|
|
if await {
|
|
tx, _ = e.CheckAwaitableTxPersisted(t)
|
|
} else {
|
|
tx, _ = e.CheckTxPersisted(t)
|
|
}
|
|
|
|
require.Equal(t, scope, tx.Signers[0].Scopes.String())
|
|
if !haveData {
|
|
return
|
|
}
|
|
|
|
line, err := e.Out.ReadString('\n')
|
|
require.NoError(t, err)
|
|
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
|
|
h, err := util.Uint160DecodeStringLE(line)
|
|
require.NoError(t, err)
|
|
|
|
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
h.StringLE(),
|
|
"getValueWithKey", "key1",
|
|
)
|
|
|
|
res := new(result.Invoke)
|
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
|
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
|
|
require.Len(t, res.Stack, 1)
|
|
require.Equal(t, []byte{12}, res.Stack[0].Value())
|
|
|
|
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
h.StringLE(),
|
|
"getValueWithKey", "key2",
|
|
)
|
|
|
|
res = new(result.Invoke)
|
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
|
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
|
|
require.Len(t, res.Stack, 1)
|
|
require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
|
|
}
|
|
|
|
deployContract(t, true, "", false)
|
|
deployContract(t, false, "Global", false)
|
|
deployContract(t, true, "Global", false)
|
|
deployContract(t, false, "", true)
|
|
deployContract(t, true, "Global", true)
|
|
deployContract(t, true, "", true)
|
|
}
|
|
|
|
func TestDeployWithSigners(t *testing.T) {
|
|
e := testcli.NewExecutor(t, true)
|
|
tmpDir := t.TempDir()
|
|
|
|
nefName := filepath.Join(tmpDir, "deploy.nef")
|
|
manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
|
|
e.Run(t, "neo-go", "contract", "compile",
|
|
"--in", "testdata/deploy/main.go",
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--out", nefName, "--manifest", manifestName)
|
|
|
|
t.Run("missing nef", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName)
|
|
})
|
|
t.Run("missing manifest", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, "required flag --manifest is empty", "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", "")
|
|
})
|
|
t.Run("corrupted data", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName,
|
|
"[", "str1")
|
|
})
|
|
t.Run("invalid data", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName,
|
|
"str1", "str2")
|
|
})
|
|
t.Run("missing wallet", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName,
|
|
"[", "str1", "str2", "]")
|
|
})
|
|
t.Run("missing RPC", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "deploy",
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName,
|
|
"[", "str1", "str2", "]")
|
|
})
|
|
e.In.WriteString(testcli.ValidatorPass + "\r")
|
|
e.Run(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName,
|
|
"--force",
|
|
"--", testcli.ValidatorAddr+":Global")
|
|
tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ")
|
|
require.Equal(t, transaction.Global, tx.Signers[0].Scopes)
|
|
}
|
|
|
|
func TestContractManifestGroups(t *testing.T) {
|
|
e := testcli.NewExecutor(t, true)
|
|
tmpDir := t.TempDir()
|
|
|
|
_, err := wallet.NewWalletFromFile(testcli.TestWalletPath)
|
|
require.NoError(t, err)
|
|
|
|
nefName := filepath.Join(tmpDir, "deploy.nef")
|
|
manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
|
|
e.Run(t, "neo-go", "contract", "compile",
|
|
"--in", "testdata/deploy/main.go", // compile single file
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--out", nefName, "--manifest", manifestName)
|
|
|
|
t.Run("missing wallet", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flags "sender, address, nef, manifest" not set`, "neo-go", "contract", "manifest", "add-group")
|
|
})
|
|
t.Run("invalid wallet", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
|
|
"--wallet", t.TempDir(), "--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount,
|
|
"--nef", nefName, "--manifest", manifestName)
|
|
})
|
|
t.Run("invalid sender", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `invalid value "not-a-sender" for flag -sender: invalid base58 digit ('-')`, "neo-go", "contract", "manifest", "add-group",
|
|
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
|
|
"--sender", "not-a-sender", "--nef", nefName, "--manifest", manifestName)
|
|
})
|
|
t.Run("invalid NEF file", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
|
|
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
|
|
"--sender", testcli.TestWalletAccount, "--nef", tmpDir, "--manifest", manifestName)
|
|
})
|
|
t.Run("corrupted NEF file", func(t *testing.T) {
|
|
f := filepath.Join(tmpDir, "invalid.nef")
|
|
require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
|
|
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
|
|
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
|
|
"--sender", testcli.TestWalletAccount, "--nef", f, "--manifest", manifestName)
|
|
})
|
|
t.Run("invalid manifest file", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
|
|
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
|
|
"--sender", testcli.TestWalletAccount, "--nef", nefName,
|
|
"--manifest", tmpDir)
|
|
})
|
|
t.Run("corrupted manifest file", func(t *testing.T) {
|
|
f := filepath.Join(tmpDir, "invalid.manifest.json")
|
|
require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
|
|
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
|
|
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
|
|
"--sender", testcli.TestWalletAccount, "--nef", nefName,
|
|
"--manifest", f)
|
|
})
|
|
t.Run("unknown account", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
|
|
"--wallet", testcli.TestWalletPath, "--address", util.Uint160{}.StringLE(),
|
|
"--sender", testcli.TestWalletAccount, "--nef", nefName,
|
|
"--manifest", manifestName)
|
|
})
|
|
cmd := []string{"neo-go", "contract", "manifest", "add-group",
|
|
"--nef", nefName, "--manifest", manifestName}
|
|
|
|
t.Run("excessive parameters", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "--wallet", testcli.TestWalletPath,
|
|
"--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount, "something")...)
|
|
})
|
|
e.In.WriteString("testpass\r")
|
|
e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath,
|
|
"--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount)...)
|
|
|
|
e.In.WriteString("testpass\r") // should override signature with the previous sender
|
|
e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath,
|
|
"--sender", testcli.ValidatorAddr, "--address", testcli.TestWalletAccount)...)
|
|
|
|
e.In.WriteString(testcli.ValidatorPass + "\r")
|
|
e.Run(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--in", nefName, "--manifest", manifestName,
|
|
"--force",
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr)
|
|
}
|
|
|
|
func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 {
|
|
return testcli.DeployContract(t, e, "testdata/verify.go", "testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
|
|
}
|
|
|
|
func TestContract_TestInvokeScript(t *testing.T) {
|
|
e := testcli.NewExecutor(t, true)
|
|
tmpDir := t.TempDir()
|
|
badNef := filepath.Join(tmpDir, "invalid.nef")
|
|
goodNef := filepath.Join(tmpDir, "deploy.nef")
|
|
manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
|
|
e.Run(t, "neo-go", "contract", "compile",
|
|
"--in", "testdata/deploy/main.go", // compile single file
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--out", goodNef, "--manifest", manifestName)
|
|
|
|
t.Run("missing in", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
|
|
})
|
|
t.Run("empty in", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, "required flag --in is empty", "neo-go", "contract", "testinvokescript", "-i", "",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
|
|
})
|
|
t.Run("empty rpc", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, "required flag --rpc-endpoint is empty", "neo-go", "contract", "testinvokescript", "-i", goodNef,
|
|
"--rpc-endpoint", "")
|
|
})
|
|
t.Run("unexisting in", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--in", badNef)
|
|
})
|
|
t.Run("invalid nef", func(t *testing.T) {
|
|
require.NoError(t, os.WriteFile(badNef, []byte("qwer"), os.ModePerm))
|
|
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--in", badNef)
|
|
})
|
|
t.Run("invalid signers", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--in", goodNef, "--", "not-a-valid-signer")
|
|
})
|
|
t.Run("no RPC endpoint", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://123456789",
|
|
"--in", goodNef)
|
|
})
|
|
t.Run("good", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--in", goodNef)
|
|
})
|
|
t.Run("good with hashed signer", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--in", goodNef, "--", util.Uint160{1, 2, 3}.StringLE())
|
|
})
|
|
t.Run("good with addressed signer", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--in", goodNef, "--", address.Uint160ToString(util.Uint160{1, 2, 3}))
|
|
})
|
|
t.Run("historic, invalid", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--historic", "bad",
|
|
"--in", goodNef)
|
|
})
|
|
t.Run("historic, index", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--historic", "0",
|
|
"--in", goodNef)
|
|
})
|
|
t.Run("historic, hash", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "contract", "testinvokescript",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--historic", e.Chain.GetHeaderHash(0).StringLE(),
|
|
"--in", goodNef)
|
|
})
|
|
}
|
|
|
|
func TestComlileAndInvokeFunction(t *testing.T) {
|
|
e := testcli.NewExecutor(t, true)
|
|
tmpDir := t.TempDir()
|
|
|
|
nefName := filepath.Join(tmpDir, "deploy.nef")
|
|
manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
|
|
e.Run(t, "neo-go", "contract", "compile",
|
|
"--in", "testdata/deploy/main.go", // compile single file
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--out", nefName, "--manifest", manifestName)
|
|
|
|
tmp := t.TempDir()
|
|
configPath := filepath.Join(tmp, "config.yaml")
|
|
cfg := config.Wallet{
|
|
Path: testcli.ValidatorWallet,
|
|
Password: testcli.ValidatorPass,
|
|
}
|
|
yml, err := yaml.Marshal(cfg)
|
|
require.NoError(t, err)
|
|
require.NoError(t, os.WriteFile(configPath, yml, 0666))
|
|
e.Run(t, "neo-go", "contract", "deploy",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--force",
|
|
"--wallet-config", configPath, "--address", testcli.ValidatorAddr,
|
|
"--in", nefName, "--manifest", manifestName)
|
|
|
|
e.CheckTxPersisted(t, "Sent invocation transaction ")
|
|
line, err := e.Out.ReadString('\n')
|
|
require.NoError(t, err)
|
|
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
|
|
h, err := util.Uint160DecodeStringLE(line)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("check calc hash", func(t *testing.T) {
|
|
// missing sender
|
|
e.RunWithErrorCheck(t, `Required flag "sender" not set`, "neo-go", "contract", "calc-hash",
|
|
"--in", nefName,
|
|
"--manifest", manifestName)
|
|
|
|
e.Run(t, "neo-go", "contract", "calc-hash",
|
|
"--sender", testcli.ValidatorAddr, "--in", nefName,
|
|
"--manifest", manifestName)
|
|
e.CheckNextLine(t, h.StringLE())
|
|
})
|
|
|
|
cmd := []string{"neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
|
|
t.Run("missing hash", func(t *testing.T) {
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
t.Run("invalid hash", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "notahash")...)
|
|
})
|
|
|
|
cmd = append(cmd, h.StringLE())
|
|
t.Run("missing method", func(t *testing.T) {
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
|
|
cmd = append(cmd, "getValue")
|
|
t.Run("invalid params", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "[")...)
|
|
})
|
|
t.Run("invalid cosigner", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "--", "notahash")...)
|
|
})
|
|
t.Run("missing RPC address", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "testinvokefunction",
|
|
h.StringLE(), "getValue")
|
|
})
|
|
|
|
e.Run(t, cmd...)
|
|
|
|
checkGetValueOut := func(str string) {
|
|
res := new(result.Invoke)
|
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
|
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
|
|
require.Len(t, res.Stack, 1)
|
|
require.Equal(t, []byte(str), res.Stack[0].Value())
|
|
}
|
|
checkGetValueOut("on create|sub create")
|
|
|
|
// deploy verification contract
|
|
hVerify := deployVerifyContract(t, e)
|
|
|
|
t.Run("real invoke", func(t *testing.T) {
|
|
cmd := []string{"neo-go", "contract", "invokefunction",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
|
|
t.Run("missing wallet", func(t *testing.T) {
|
|
cmd := append(cmd, h.StringLE(), "getValue")
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
t.Run("non-existent wallet", func(t *testing.T) {
|
|
cmd := append(cmd, "--wallet", filepath.Join(tmpDir, "not.exists"),
|
|
h.StringLE(), "getValue")
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
t.Run("corrupted wallet", func(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
tmpPath := filepath.Join(tmp, "wallet.json")
|
|
require.NoError(t, os.WriteFile(tmpPath, []byte("{"), os.ModePerm))
|
|
|
|
cmd := append(cmd, "--wallet", tmpPath,
|
|
h.StringLE(), "getValue")
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
t.Run("non-existent address", func(t *testing.T) {
|
|
cmd := append(cmd, "--wallet", testcli.ValidatorWallet,
|
|
"--address", random.Uint160().StringLE(),
|
|
h.StringLE(), "getValue")
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
t.Run("invalid password", func(t *testing.T) {
|
|
e.In.WriteString("invalid_password\r")
|
|
cmd := append(cmd, "--wallet", testcli.ValidatorWallet,
|
|
h.StringLE(), "getValue")
|
|
e.RunWithError(t, cmd...)
|
|
})
|
|
t.Run("good: default address", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.In.WriteString("y\r")
|
|
e.Run(t, append(cmd, "--wallet", testcli.ValidatorWallet, h.StringLE(), "getValue")...)
|
|
})
|
|
t.Run("good: from wallet config", func(t *testing.T) {
|
|
e.In.WriteString("y\r")
|
|
e.Run(t, append(cmd, "--wallet-config", configPath, h.StringLE(), "getValue")...)
|
|
})
|
|
|
|
cmd = append(cmd, "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr)
|
|
t.Run("cancelled", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.In.WriteString("n\r")
|
|
e.RunWithError(t, append(cmd, h.StringLE(), "getValue")...)
|
|
})
|
|
t.Run("confirmed", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.In.WriteString("y\r")
|
|
e.Run(t, append(cmd, h.StringLE(), "getValue")...)
|
|
})
|
|
|
|
t.Run("failind method", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.In.WriteString("y\r")
|
|
e.RunWithError(t, append(cmd, h.StringLE(), "fail")...)
|
|
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, append(cmd, "--force", h.StringLE(), "fail")...)
|
|
})
|
|
|
|
t.Run("cosigner is deployed contract", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.In.WriteString("y\r")
|
|
e.Run(t, append(cmd, h.StringLE(), "getValue",
|
|
"--", testcli.ValidatorAddr, hVerify.StringLE())...)
|
|
})
|
|
|
|
t.Run("with await", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, append(cmd, "--force", "--await", h.StringLE(), "getValue")...)
|
|
e.CheckAwaitableTxPersisted(t)
|
|
})
|
|
})
|
|
|
|
t.Run("real invoke and save tx", func(t *testing.T) {
|
|
txout := filepath.Join(tmpDir, "test_contract_tx.json")
|
|
|
|
cmd = []string{"neo-go", "contract", "invokefunction",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
|
"--out", txout,
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
}
|
|
|
|
t.Run("without cosigner", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, append(cmd, hVerify.StringLE(), "verify")...)
|
|
})
|
|
|
|
t.Run("with cosigner", func(t *testing.T) {
|
|
t.Run("cosigner is sender (none)", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.RunWithError(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":None")...)
|
|
})
|
|
t.Run("cosigner is sender (customcontract)", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":CustomContracts:"+h.StringLE())...)
|
|
})
|
|
t.Run("cosigner is sender (global)", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":Global")...)
|
|
})
|
|
|
|
acc, err := wallet.NewAccount()
|
|
require.NoError(t, err)
|
|
pk, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
err = acc.ConvertMultisig(2, keys.PublicKeys{acc.PublicKey(), pk.PublicKey()})
|
|
require.NoError(t, err)
|
|
|
|
t.Run("cosigner is multisig account", func(t *testing.T) {
|
|
t.Run("missing in the wallet", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", acc.Address)...)
|
|
})
|
|
|
|
t.Run("good", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", testcli.MultisigAddr)...)
|
|
})
|
|
})
|
|
|
|
t.Run("cosigner is deployed contract", func(t *testing.T) {
|
|
t.Run("missing in the wallet", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", h.StringLE())...)
|
|
})
|
|
|
|
t.Run("good", func(t *testing.T) {
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", hVerify.StringLE())...)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("test Storage.Find", func(t *testing.T) {
|
|
cmd := []string{"neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
|
|
h.StringLE(), "testFind"}
|
|
|
|
t.Run("keys only", func(t *testing.T) {
|
|
e.Run(t, append(cmd, strconv.FormatInt(storage.FindKeysOnly, 10))...)
|
|
res := new(result.Invoke)
|
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
|
require.Equal(t, vmstate.Halt.String(), res.State)
|
|
require.Len(t, res.Stack, 1)
|
|
require.Equal(t, []stackitem.Item{
|
|
stackitem.Make("findkey1"),
|
|
stackitem.Make("findkey2"),
|
|
}, res.Stack[0].Value())
|
|
})
|
|
t.Run("both", func(t *testing.T) {
|
|
e.Run(t, append(cmd, strconv.FormatInt(storage.FindDefault, 10))...)
|
|
res := new(result.Invoke)
|
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
|
require.Equal(t, vmstate.Halt.String(), res.State)
|
|
require.Len(t, res.Stack, 1)
|
|
|
|
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
|
require.True(t, ok)
|
|
require.Len(t, arr, 2)
|
|
require.Equal(t, []stackitem.Item{
|
|
stackitem.Make("findkey1"), stackitem.Make("value1"),
|
|
}, arr[0].Value())
|
|
require.Equal(t, []stackitem.Item{
|
|
stackitem.Make("findkey2"), stackitem.Make("value2"),
|
|
}, arr[1].Value())
|
|
})
|
|
})
|
|
|
|
var (
|
|
hashBeforeUpdate util.Uint256
|
|
indexBeforeUpdate uint32
|
|
indexAfterUpdate uint32
|
|
stateBeforeUpdate util.Uint256
|
|
)
|
|
t.Run("Update", func(t *testing.T) {
|
|
nefName := filepath.Join(tmpDir, "updated.nef")
|
|
manifestName := filepath.Join(tmpDir, "updated.manifest.json")
|
|
e.Run(t, "neo-go", "contract", "compile",
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--in", "testdata/deploy/", // compile all files in dir
|
|
"--out", nefName, "--manifest", manifestName)
|
|
|
|
t.Cleanup(func() {
|
|
os.Remove(nefName)
|
|
os.Remove(manifestName)
|
|
})
|
|
|
|
rawNef, err := os.ReadFile(nefName)
|
|
require.NoError(t, err)
|
|
rawManifest, err := os.ReadFile(manifestName)
|
|
require.NoError(t, err)
|
|
|
|
indexBeforeUpdate = e.Chain.BlockHeight()
|
|
hashBeforeUpdate = e.Chain.CurrentHeaderHash()
|
|
mptBeforeUpdate, err := e.Chain.GetStateRoot(indexBeforeUpdate)
|
|
require.NoError(t, err)
|
|
stateBeforeUpdate = mptBeforeUpdate.Root
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, "neo-go", "contract", "invokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
|
|
"--force",
|
|
h.StringLE(), "update",
|
|
"bytes:"+hex.EncodeToString(rawNef),
|
|
"bytes:"+hex.EncodeToString(rawManifest),
|
|
)
|
|
e.CheckTxPersisted(t, "Sent invocation transaction ")
|
|
|
|
indexAfterUpdate = e.Chain.BlockHeight()
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
h.StringLE(), "getValue")
|
|
checkGetValueOut("on update|sub update")
|
|
})
|
|
t.Run("historic", func(t *testing.T) {
|
|
t.Run("bad ref", func(t *testing.T) {
|
|
e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--historic", "bad",
|
|
h.StringLE(), "getValue")
|
|
})
|
|
for name, ref := range map[string]string{
|
|
"by index": strconv.FormatUint(uint64(indexBeforeUpdate), 10),
|
|
"by block hash": hashBeforeUpdate.StringLE(),
|
|
"by state hash": stateBeforeUpdate.StringLE(),
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--historic", ref,
|
|
h.StringLE(), "getValue")
|
|
})
|
|
checkGetValueOut("on create|sub create")
|
|
}
|
|
t.Run("updated historic", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
|
"--historic", strconv.FormatUint(uint64(indexAfterUpdate), 10),
|
|
h.StringLE(), "getValue")
|
|
checkGetValueOut("on update|sub update")
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestContractInspect(t *testing.T) {
|
|
e := testcli.NewExecutor(t, false)
|
|
const srcPath = "testdata/deploy/main.go"
|
|
tmpDir := t.TempDir()
|
|
|
|
nefName := filepath.Join(tmpDir, "deploy.nef")
|
|
manifestName := filepath.Join(tmpDir, "deploy.manifest.json")
|
|
e.Run(t, "neo-go", "contract", "compile",
|
|
"--in", srcPath,
|
|
"--config", "testdata/deploy/neo-go.yml",
|
|
"--out", nefName, "--manifest", manifestName)
|
|
|
|
cmd := []string{"neo-go", "contract", "inspect"}
|
|
t.Run("missing input", func(t *testing.T) {
|
|
e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...)
|
|
})
|
|
t.Run("with raw '.go'", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "--in", srcPath)...)
|
|
e.Run(t, append(cmd, "--in", srcPath, "--compile")...)
|
|
require.True(t, strings.Contains(e.Out.String(), "SYSCALL"))
|
|
})
|
|
t.Run("with nef", func(t *testing.T) {
|
|
e.RunWithError(t, append(cmd, "--in", nefName, "--compile")...)
|
|
e.RunWithError(t, append(cmd, "--in", filepath.Join(tmpDir, "not.exists"))...)
|
|
e.RunWithError(t, append(cmd, "--in", nefName, "something")...)
|
|
e.Run(t, append(cmd, "--in", nefName)...)
|
|
require.True(t, strings.Contains(e.Out.String(), "SYSCALL"))
|
|
})
|
|
}
|
|
|
|
func TestCompileExamples(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
const examplePath = "../../examples"
|
|
infos, err := os.ReadDir(examplePath)
|
|
require.NoError(t, err)
|
|
|
|
e := testcli.NewExecutor(t, false)
|
|
|
|
for _, info := range infos {
|
|
if !info.IsDir() {
|
|
// example smart contracts are located in the `/examples` subdirectories, but
|
|
// there are also a couple of files inside the `/examples` which doesn't need to be compiled
|
|
continue
|
|
}
|
|
if info.Name() == "zkp" {
|
|
// A set of special ZKP-related examples, they have their own tests.
|
|
continue
|
|
}
|
|
t.Run(info.Name(), func(t *testing.T) {
|
|
infos, err := os.ReadDir(filepath.Join(examplePath, info.Name()))
|
|
require.NoError(t, err)
|
|
require.False(t, len(infos) == 0, "detected smart contract folder with no contract in it")
|
|
|
|
outF := filepath.Join(tmpDir, info.Name()+".nef")
|
|
manifestF := filepath.Join(tmpDir, info.Name()+".manifest.json")
|
|
bindingF := filepath.Join(tmpDir, info.Name()+".binding.yml")
|
|
wrapperF := filepath.Join(tmpDir, info.Name()+".go")
|
|
rpcWrapperF := filepath.Join(tmpDir, info.Name()+".rpc.go")
|
|
|
|
cfgName := filterFilename(infos, ".yml")
|
|
opts := []string{
|
|
"neo-go", "contract", "compile",
|
|
"--in", filepath.Join(examplePath, info.Name()),
|
|
"--out", outF,
|
|
"--manifest", manifestF,
|
|
"--config", filepath.Join(examplePath, info.Name(), cfgName),
|
|
"--bindings", bindingF,
|
|
}
|
|
e.Run(t, opts...)
|
|
|
|
if info.Name() == "storage" {
|
|
rawM, err := os.ReadFile(manifestF)
|
|
require.NoError(t, err)
|
|
|
|
m := new(manifest.Manifest)
|
|
require.NoError(t, json.Unmarshal(rawM, m))
|
|
|
|
require.Nil(t, m.ABI.GetMethod("getDefault", 0))
|
|
require.NotNil(t, m.ABI.GetMethod("get", 0))
|
|
require.NotNil(t, m.ABI.GetMethod("get", 1))
|
|
|
|
require.Nil(t, m.ABI.GetMethod("putDefault", 1))
|
|
require.NotNil(t, m.ABI.GetMethod("put", 1))
|
|
require.NotNil(t, m.ABI.GetMethod("put", 2))
|
|
}
|
|
e.Run(t, "neo-go", "contract", "generate-wrapper",
|
|
"--manifest", manifestF,
|
|
"--config", bindingF,
|
|
"--out", wrapperF,
|
|
"--hash", "0x00112233445566778899aabbccddeeff00112233")
|
|
e.Run(t, "neo-go", "contract", "generate-rpcwrapper",
|
|
"--manifest", manifestF,
|
|
"--config", bindingF,
|
|
"--out", rpcWrapperF,
|
|
"--hash", "0x00112233445566778899aabbccddeeff00112233")
|
|
})
|
|
}
|
|
|
|
t.Run("invalid manifest", func(t *testing.T) {
|
|
const dir = "./testdata/"
|
|
for _, name := range []string{"invalid1", "invalid2", "invalid3", "invalid4"} {
|
|
outF := filepath.Join(tmpDir, name+".nef")
|
|
manifestF := filepath.Join(tmpDir, name+".manifest.json")
|
|
e.RunWithError(t, "neo-go", "contract", "compile",
|
|
"--in", filepath.Join(dir, name),
|
|
"--out", outF,
|
|
"--manifest", manifestF,
|
|
"--config", filepath.Join(dir, name, "invalid.yml"),
|
|
)
|
|
}
|
|
})
|
|
}
|
|
|
|
func filterFilename(infos []os.DirEntry, ext string) string {
|
|
for _, info := range infos {
|
|
if !info.IsDir() {
|
|
name := info.Name()
|
|
if strings.HasSuffix(name, ext) {
|
|
return name
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TestContractCompile_NEFSizeCheck(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
e := testcli.NewExecutor(t, false)
|
|
|
|
src := `package nefconstraints
|
|
var data = "%s"
|
|
|
|
func Main() string {
|
|
return data
|
|
}`
|
|
data := make([]byte, stackitem.MaxSize-10)
|
|
for i := range data {
|
|
data[i] = byte('a')
|
|
}
|
|
|
|
in := filepath.Join(tmpDir, "main.go")
|
|
cfg := filepath.Join(tmpDir, "main.yml")
|
|
require.NoError(t, os.WriteFile(cfg, []byte("name: main"), os.ModePerm))
|
|
require.NoError(t, os.WriteFile(in, []byte(fmt.Sprintf(src, data)), os.ModePerm))
|
|
|
|
e.RunWithError(t, "neo-go", "contract", "compile", "--in", in)
|
|
require.NoFileExists(t, filepath.Join(tmpDir, "main.nef"))
|
|
}
|