cli: properly handle run
VM CLI command
Properly load the provided method using NEF and hash specified. It allows to have NEF properly set in the VM context and handle CALLT instruction correctly. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
35c3518b37
commit
b6b80f3abf
4 changed files with 343 additions and 136 deletions
|
@ -281,6 +281,97 @@ func (e *executor) checkSlot(t *testing.T, items ...any) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRun_WithNewVMContextAndBreakpoints(t *testing.T) {
|
||||
t.Run("contract without init", func(t *testing.T) {
|
||||
src := `package kek
|
||||
func Main(a, b int) int {
|
||||
var c = a + b
|
||||
return c + 5
|
||||
}`
|
||||
tmpDir := t.TempDir()
|
||||
filename := prepareLoadgoSrc(t, tmpDir, src)
|
||||
|
||||
e := newTestVMCLI(t)
|
||||
e.runProgWithTimeout(t, 10*time.Second,
|
||||
"loadgo "+filename,
|
||||
"break 8",
|
||||
"run main 3 5",
|
||||
"run",
|
||||
)
|
||||
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkNextLine(t, "breakpoint added at instruction 8")
|
||||
e.checkNextLine(t, "at breakpoint 8 (PUSH5)*")
|
||||
e.checkStack(t, 13)
|
||||
})
|
||||
t.Run("contract with init", func(t *testing.T) {
|
||||
src := `package kek
|
||||
var I = 5
|
||||
func Main(a, b int) int {
|
||||
var c = a + b
|
||||
return c + I
|
||||
}`
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
filename := prepareLoadgoSrc(t, tmpDir, src)
|
||||
|
||||
e := newTestVMCLI(t)
|
||||
e.runProgWithTimeout(t, 10*time.Second,
|
||||
"loadgo "+filename,
|
||||
"break 10",
|
||||
"run main 3 5",
|
||||
"run",
|
||||
)
|
||||
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkNextLine(t, "breakpoint added at instruction 10")
|
||||
e.checkNextLine(t, "at breakpoint 10 (ADD)*")
|
||||
e.checkStack(t, 13)
|
||||
})
|
||||
}
|
||||
|
||||
// prepareLoadgoSrc prepares provided SC source file for loading into VM via `loadgo` command.
|
||||
func prepareLoadgoSrc(t *testing.T, tmpDir, src string) string {
|
||||
filename := filepath.Join(tmpDir, "vmtestcontract.go")
|
||||
require.NoError(t, os.WriteFile(filename, []byte(src), os.ModePerm))
|
||||
filename = "'" + filename + "'"
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
goMod := []byte(`module test.example/kek
|
||||
require (
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0
|
||||
)
|
||||
replace github.com/nspcc-dev/neo-go/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + `
|
||||
go 1.18`)
|
||||
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm))
|
||||
return filename
|
||||
}
|
||||
|
||||
// prepareLoadnefSrc compiles provided SC source and prepares NEF and manifest for loading into VM
|
||||
// via `loadnef` command. It returns the name of manifest and NEF files ready to be used in CLI
|
||||
// commands.
|
||||
func prepareLoadnefSrc(t *testing.T, tmpDir, src string) (string, string) {
|
||||
config.Version = "0.92.0-test"
|
||||
|
||||
nefFile, di, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
|
||||
require.NoError(t, err)
|
||||
filename := filepath.Join(tmpDir, "vmtestcontract.nef")
|
||||
rawNef, err := nefFile.Bytes()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile(filename, rawNef, os.ModePerm))
|
||||
m, err := di.ConvertToManifest(&compiler.Options{})
|
||||
require.NoError(t, err)
|
||||
manifestFile := filepath.Join(tmpDir, "vmtestcontract.manifest.json")
|
||||
rawManifest, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
|
||||
|
||||
manifestFile = "'" + manifestFile + "'"
|
||||
filename = "'" + filename + "'"
|
||||
|
||||
return manifestFile, filename
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
script := []byte{byte(opcode.PUSH3), byte(opcode.PUSH4), byte(opcode.ADD)}
|
||||
|
||||
|
@ -378,10 +469,11 @@ func TestLoad(t *testing.T) {
|
|||
return a * b
|
||||
}
|
||||
}`
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
checkLoadgo := func(t *testing.T, tName, cName, cErrName string) {
|
||||
t.Run("loadgo "+tName, func(t *testing.T) {
|
||||
t.Run("loadgo", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
checkLoadgo := func(t *testing.T, cName, cErrName string) {
|
||||
filename := filepath.Join(tmpDir, cName)
|
||||
require.NoError(t, os.WriteFile(filename, []byte(src), os.ModePerm))
|
||||
filename = "'" + filename + "'"
|
||||
|
@ -403,46 +495,34 @@ go 1.18`)
|
|||
e.checkNextLine(t, "Error:")
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkStack(t, 8)
|
||||
}
|
||||
|
||||
t.Run("simple", func(t *testing.T) {
|
||||
checkLoadgo(t, "vmtestcontract.go", "vmtestcontract_err.go")
|
||||
})
|
||||
t.Run("utf-8 with spaces", func(t *testing.T) {
|
||||
checkLoadgo(t, "тестовый контракт.go", "тестовый контракт с ошибкой.go")
|
||||
})
|
||||
}
|
||||
|
||||
checkLoadgo(t, "simple", "vmtestcontract.go", "vmtestcontract_err.go")
|
||||
checkLoadgo(t, "utf-8 with spaces", "тестовый контракт.go", "тестовый контракт с ошибкой.go")
|
||||
|
||||
prepareLoadgoSrc := func(t *testing.T, srcAllowNotify string) string {
|
||||
filename := filepath.Join(tmpDir, "vmtestcontract.go")
|
||||
require.NoError(t, os.WriteFile(filename, []byte(srcAllowNotify), os.ModePerm))
|
||||
filename = "'" + filename + "'"
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
goMod := []byte(`module test.example/kek
|
||||
require (
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0
|
||||
)
|
||||
replace github.com/nspcc-dev/neo-go/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + `
|
||||
go 1.18`)
|
||||
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm))
|
||||
return filename
|
||||
}
|
||||
t.Run("loadgo, check calling flags", func(t *testing.T) {
|
||||
srcAllowNotify := `package kek
|
||||
t.Run("check calling flags", func(t *testing.T) {
|
||||
srcAllowNotify := `package kek
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
func Main() int {
|
||||
runtime.Log("Hello, world!")
|
||||
return 1
|
||||
}
|
||||
`
|
||||
filename := prepareLoadgoSrc(t, srcAllowNotify)
|
||||
filename := prepareLoadgoSrc(t, tmpDir, srcAllowNotify)
|
||||
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename,
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkStack(t, 1)
|
||||
})
|
||||
t.Run("loadgo, check signers", func(t *testing.T) {
|
||||
srcCheckWitness := `package kek
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename,
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkStack(t, 1)
|
||||
})
|
||||
t.Run("check signers", func(t *testing.T) {
|
||||
srcCheckWitness := `package kek
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
|
||||
|
@ -452,90 +532,78 @@ go 1.18`)
|
|||
return runtime.CheckWitness(owner)
|
||||
}
|
||||
`
|
||||
filename := prepareLoadgoSrc(t, srcCheckWitness)
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator,
|
||||
"loadgo "+filename+" "+"not-a-separator",
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" not-a-signer",
|
||||
)
|
||||
e.checkError(t, ErrInvalidParameter)
|
||||
e.checkError(t, ErrInvalidParameter)
|
||||
e.checkError(t, ErrInvalidParameter)
|
||||
})
|
||||
t.Run("address", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAddress, // owner:DefaultScope => true
|
||||
"run main",
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAddress+":None", // owner:None => false
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, true)
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, false)
|
||||
})
|
||||
t.Run("string LE", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true
|
||||
"run main",
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+"0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, true)
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, true)
|
||||
})
|
||||
t.Run("nonwitnessed signer", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, false)
|
||||
filename := prepareLoadgoSrc(t, tmpDir, srcCheckWitness)
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator,
|
||||
"loadgo "+filename+" "+"not-a-separator",
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" not-a-signer",
|
||||
)
|
||||
e.checkError(t, ErrInvalidParameter)
|
||||
e.checkError(t, ErrInvalidParameter)
|
||||
e.checkError(t, ErrInvalidParameter)
|
||||
})
|
||||
t.Run("address", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAddress, // owner:DefaultScope => true
|
||||
"run main",
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAddress+":None", // owner:None => false
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, true)
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, false)
|
||||
})
|
||||
t.Run("string LE", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true
|
||||
"run main",
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+"0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, true)
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, true)
|
||||
})
|
||||
t.Run("nonwitnessed signer", func(t *testing.T) {
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename+" "+cmdargs.CosignersSeparator+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false
|
||||
"run main")
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("loadnef", func(t *testing.T) {
|
||||
config.Version = "0.92.0-test"
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
nefFile, di, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
|
||||
require.NoError(t, err)
|
||||
filename := filepath.Join(tmpDir, "vmtestcontract.nef")
|
||||
rawNef, err := nefFile.Bytes()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile(filename, rawNef, os.ModePerm))
|
||||
m, err := di.ConvertToManifest(&compiler.Options{})
|
||||
require.NoError(t, err)
|
||||
manifestFile := filepath.Join(tmpDir, "vmtestcontract.manifest.json")
|
||||
rawManifest, err := json.Marshal(m)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
|
||||
manifestFile, nefFile := prepareLoadnefSrc(t, tmpDir, src)
|
||||
filenameErr := filepath.Join(tmpDir, "vmtestcontract_err.nef")
|
||||
require.NoError(t, os.WriteFile(filenameErr, append([]byte{1, 2, 3, 4}, rawNef...), os.ModePerm))
|
||||
require.NoError(t, os.WriteFile(filenameErr, []byte{1, 2, 3, 4}, os.ModePerm))
|
||||
notExists := filepath.Join(tmpDir, "notexists.json")
|
||||
|
||||
manifestFile = "'" + manifestFile + "'"
|
||||
filename = "'" + filename + "'"
|
||||
filenameErr = "'" + filenameErr + "'"
|
||||
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadnef",
|
||||
"loadnef "+filenameErr+" "+manifestFile,
|
||||
"loadnef "+filename+" "+notExists,
|
||||
"loadnef "+filename+" "+filename,
|
||||
"loadnef "+filename+" "+manifestFile,
|
||||
"loadnef "+nefFile+" "+notExists,
|
||||
"loadnef "+nefFile+" "+nefFile,
|
||||
"loadnef "+nefFile+" "+manifestFile,
|
||||
"run main add 3 5",
|
||||
"loadnef "+filename,
|
||||
"loadnef "+nefFile,
|
||||
"run main add 3 5",
|
||||
"loadnef "+filename+" "+cmdargs.CosignersSeparator,
|
||||
"loadnef "+filename+" "+manifestFile+" "+cmdargs.CosignersSeparator,
|
||||
"loadnef "+filename+" "+manifestFile+" "+"not-a-separator",
|
||||
"loadnef "+filename+" "+cmdargs.CosignersSeparator+" "+util.Uint160{1, 2, 3}.StringLE(),
|
||||
"loadnef "+nefFile+" "+cmdargs.CosignersSeparator,
|
||||
"loadnef "+nefFile+" "+manifestFile+" "+cmdargs.CosignersSeparator,
|
||||
"loadnef "+nefFile+" "+manifestFile+" "+"not-a-separator",
|
||||
"loadnef "+nefFile+" "+cmdargs.CosignersSeparator+" "+util.Uint160{1, 2, 3}.StringLE(),
|
||||
"run main add 3 5",
|
||||
"loadnef "+filename+" "+manifestFile+" "+cmdargs.CosignersSeparator+" "+util.Uint160{1, 2, 3}.StringLE(),
|
||||
"loadnef "+nefFile+" "+manifestFile+" "+cmdargs.CosignersSeparator+" "+util.Uint160{1, 2, 3}.StringLE(),
|
||||
"run main add 3 5",
|
||||
)
|
||||
|
||||
|
@ -557,6 +625,72 @@ go 1.18`)
|
|||
})
|
||||
}
|
||||
|
||||
func TestLoad_RunWithCALLT(t *testing.T) {
|
||||
// Our smart compiler will generate CALLT instruction for the following StdLib call:
|
||||
src := `package kek
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
||||
func Main() int {
|
||||
return std.Atoi("123", 10)
|
||||
}`
|
||||
|
||||
t.Run("loadgo", func(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
filename := prepareLoadgoSrc(t, tmp, src)
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadgo "+filename,
|
||||
"run main",
|
||||
)
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkStack(t, 123)
|
||||
})
|
||||
|
||||
t.Run("loadnef", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
manifestFile, nefFile := prepareLoadnefSrc(t, tmpDir, src)
|
||||
|
||||
e := newTestVMCLI(t)
|
||||
e.runProg(t,
|
||||
"loadnef "+nefFile+" "+manifestFile,
|
||||
"run main",
|
||||
)
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkStack(t, 123)
|
||||
})
|
||||
|
||||
t.Run("loaddeployed", func(t *testing.T) {
|
||||
// We'll use `Runtime example` example contract which has a call to native Management
|
||||
// inside performed via CALLT instruction (`destroy` method).
|
||||
e := newTestVMClIWithState(t)
|
||||
var (
|
||||
cH util.Uint160
|
||||
cName = "Runtime example"
|
||||
bc = e.cli.chain
|
||||
)
|
||||
for i := int32(1); ; i++ {
|
||||
h, err := bc.GetContractScriptHash(i)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
cs := bc.GetContractState(h)
|
||||
if cs == nil {
|
||||
break
|
||||
}
|
||||
if cs.Manifest.Name == cName {
|
||||
cH = cs.Hash
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotEmpty(t, cH, fmt.Sprintf("failed to locate `%s` example contract with CALLT usage in the simple chain", cName))
|
||||
e.runProg(t,
|
||||
"loaddeployed "+cH.StringLE()+" -- NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB:Global", // the contract's owner got from the contract's code.
|
||||
"run destroy",
|
||||
)
|
||||
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||
e.checkStack(t) // Nothing on stack, successful execution.
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunWithDifferentArguments(t *testing.T) {
|
||||
src := `package kek
|
||||
var a = 1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue