forked from TrueCloudLab/neoneo-go
vm: allow to provide state for VM CLI
Close #2528. Also, add new simple testchain as an analogue for basicchain.
This commit is contained in:
parent
0b717b0c22
commit
513821cfff
4 changed files with 186 additions and 25 deletions
|
@ -19,20 +19,27 @@ import (
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/kballard/go-shellquote"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
vmKey = "vm"
|
chainKey = "chain"
|
||||||
|
icKey = "ic"
|
||||||
manifestKey = "manifest"
|
manifestKey = "manifest"
|
||||||
exitFuncKey = "exitFunc"
|
exitFuncKey = "exitFunc"
|
||||||
readlineInstanceKey = "readlineKey"
|
readlineInstanceKey = "readlineKey"
|
||||||
|
@ -245,26 +252,20 @@ var (
|
||||||
|
|
||||||
// VMCLI object for interacting with the VM.
|
// VMCLI object for interacting with the VM.
|
||||||
type VMCLI struct {
|
type VMCLI struct {
|
||||||
vm *vm.VM
|
chain *core.Blockchain
|
||||||
shell *cli.App
|
shell *cli.App
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new VMCLI object.
|
// NewWithConfig returns new VMCLI instance using provided config and (optionally)
|
||||||
func New() *VMCLI {
|
// provided node config for state-backed VM.
|
||||||
return NewWithConfig(true, os.Exit, &readline.Config{
|
func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg config.Config) (*VMCLI, error) {
|
||||||
Prompt: "\033[32mNEO-GO-VM >\033[0m ", // green prompt ^^
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWithConfig returns new VMCLI instance using provided config.
|
|
||||||
func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config) *VMCLI {
|
|
||||||
if c.AutoComplete == nil {
|
if c.AutoComplete == nil {
|
||||||
// Autocomplete commands/flags on TAB.
|
// Autocomplete commands/flags on TAB.
|
||||||
c.AutoComplete = completer
|
c.AutoComplete = completer
|
||||||
}
|
}
|
||||||
l, err := readline.NewEx(c)
|
l, err := readline.NewEx(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, fmt.Errorf("failed to create readline instance: %w", err)
|
||||||
}
|
}
|
||||||
ctl := cli.NewApp()
|
ctl := cli.NewApp()
|
||||||
ctl.Name = "VM CLI"
|
ctl.Name = "VM CLI"
|
||||||
|
@ -284,20 +285,41 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config) *VM
|
||||||
|
|
||||||
ctl.Commands = commands
|
ctl.Commands = commands
|
||||||
|
|
||||||
|
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
writeErr(ctl.ErrWriter, fmt.Errorf("failed to open DB, clean in-memory storage will be used: %w", err))
|
||||||
|
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
|
||||||
|
store = storage.NewMemoryStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
exitF := func(i int) {
|
||||||
|
_ = store.Close()
|
||||||
|
onExit(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
log := zap.NewNop()
|
||||||
|
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration, log)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1)
|
||||||
|
}
|
||||||
|
// Do not run chain, we need only state-related functionality from it.
|
||||||
|
ic := chain.GetTestVM(trigger.Application, nil, nil)
|
||||||
|
|
||||||
vmcli := VMCLI{
|
vmcli := VMCLI{
|
||||||
vm: vm.New(),
|
chain: chain,
|
||||||
shell: ctl,
|
shell: ctl,
|
||||||
}
|
}
|
||||||
|
|
||||||
vmcli.shell.Metadata = map[string]interface{}{
|
vmcli.shell.Metadata = map[string]interface{}{
|
||||||
vmKey: vmcli.vm,
|
chainKey: chain,
|
||||||
|
icKey: ic,
|
||||||
manifestKey: new(manifest.Manifest),
|
manifestKey: new(manifest.Manifest),
|
||||||
exitFuncKey: onExit,
|
exitFuncKey: exitF,
|
||||||
readlineInstanceKey: l,
|
readlineInstanceKey: l,
|
||||||
printLogoKey: printLogotype,
|
printLogoKey: printLogotype,
|
||||||
}
|
}
|
||||||
changePrompt(vmcli.shell)
|
changePrompt(vmcli.shell)
|
||||||
return &vmcli
|
return &vmcli, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExitFuncFromContext(app *cli.App) func(int) {
|
func getExitFuncFromContext(app *cli.App) func(int) {
|
||||||
|
@ -309,12 +331,15 @@ func getReadlineInstanceFromContext(app *cli.App) *readline.Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVMFromContext(app *cli.App) *vm.VM {
|
func getVMFromContext(app *cli.App) *vm.VM {
|
||||||
return app.Metadata[vmKey].(*vm.VM)
|
return getInteropContextFromContext(app).VM
|
||||||
}
|
}
|
||||||
|
|
||||||
func setVMInContext(app *cli.App, v *vm.VM) {
|
func getChainFromContext(app *cli.App) *core.Blockchain {
|
||||||
old := getVMFromContext(app)
|
return app.Metadata[chainKey].(*core.Blockchain)
|
||||||
*old = *v
|
}
|
||||||
|
|
||||||
|
func getInteropContextFromContext(app *cli.App) *interop.Context {
|
||||||
|
return app.Metadata[icKey].(*interop.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getManifestFromContext(app *cli.App) *manifest.Manifest {
|
func getManifestFromContext(app *cli.App) *manifest.Manifest {
|
||||||
|
@ -325,6 +350,10 @@ func getPrintLogoFromContext(app *cli.App) bool {
|
||||||
return app.Metadata[printLogoKey].(bool)
|
return app.Metadata[printLogoKey].(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setInteropContextInContext(app *cli.App, ic *interop.Context) {
|
||||||
|
app.Metadata[icKey] = ic
|
||||||
|
}
|
||||||
|
|
||||||
func setManifestInContext(app *cli.App, m *manifest.Manifest) {
|
func setManifestInContext(app *cli.App, m *manifest.Manifest) {
|
||||||
old := getManifestFromContext(app)
|
old := getManifestFromContext(app)
|
||||||
*old = *m
|
*old = *m
|
||||||
|
@ -340,6 +369,7 @@ func checkVMIsReady(app *cli.App) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleExit(c *cli.Context) error {
|
func handleExit(c *cli.Context) error {
|
||||||
|
finalizeInteropContext(c.App)
|
||||||
l := getReadlineInstanceFromContext(c.App)
|
l := getReadlineInstanceFromContext(c.App)
|
||||||
_ = l.Close()
|
_ = l.Close()
|
||||||
exit := getExitFuncFromContext(c.App)
|
exit := getExitFuncFromContext(c.App)
|
||||||
|
@ -419,6 +449,7 @@ func handleSlots(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadNEF(c *cli.Context) error {
|
func handleLoadNEF(c *cli.Context) error {
|
||||||
|
resetInteropContext(c.App)
|
||||||
v := getVMFromContext(c.App)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
|
@ -438,6 +469,7 @@ func handleLoadNEF(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadBase64(c *cli.Context) error {
|
func handleLoadBase64(c *cli.Context) error {
|
||||||
|
resetInteropContext(c.App)
|
||||||
v := getVMFromContext(c.App)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -454,6 +486,7 @@ func handleLoadBase64(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadHex(c *cli.Context) error {
|
func handleLoadHex(c *cli.Context) error {
|
||||||
|
resetInteropContext(c.App)
|
||||||
v := getVMFromContext(c.App)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -470,6 +503,7 @@ func handleLoadHex(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadGo(c *cli.Context) error {
|
func handleLoadGo(c *cli.Context) error {
|
||||||
|
resetInteropContext(c.App)
|
||||||
v := getVMFromContext(c.App)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -496,11 +530,26 @@ func handleLoadGo(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleReset(c *cli.Context) error {
|
func handleReset(c *cli.Context) error {
|
||||||
setVMInContext(c.App, vm.New())
|
resetInteropContext(c.App)
|
||||||
changePrompt(c.App)
|
changePrompt(c.App)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finalizeInteropContext calls finalizer for the current interop context.
|
||||||
|
func finalizeInteropContext(app *cli.App) {
|
||||||
|
ic := getInteropContextFromContext(app)
|
||||||
|
ic.Finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetInteropContext calls finalizer for current interop context and replaces
|
||||||
|
// it with the newly created one.
|
||||||
|
func resetInteropContext(app *cli.App) {
|
||||||
|
finalizeInteropContext(app)
|
||||||
|
bc := getChainFromContext(app)
|
||||||
|
newIc := bc.GetTestVM(trigger.Application, nil, nil)
|
||||||
|
setInteropContextInContext(app, newIc)
|
||||||
|
}
|
||||||
|
|
||||||
func getManifestFromFile(name string) (*manifest.Manifest, error) {
|
func getManifestFromFile(name string) (*manifest.Manifest, error) {
|
||||||
bs, err := os.ReadFile(name)
|
bs, err := os.ReadFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -15,12 +15,18 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/basicchain"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -64,12 +70,27 @@ func newTestVMCLI(t *testing.T) *executor {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestVMCLIWithLogo(t *testing.T, printLogo bool) *executor {
|
func newTestVMCLIWithLogo(t *testing.T, printLogo bool) *executor {
|
||||||
|
return newTestVMCLIWithLogoAndCustomConfig(t, printLogo, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestVMCLIWithLogoAndCustomConfig(t *testing.T, printLogo bool, cfg *config.Config) *executor {
|
||||||
e := &executor{
|
e := &executor{
|
||||||
in: &readCloser{Buffer: *bytes.NewBuffer(nil)},
|
in: &readCloser{Buffer: *bytes.NewBuffer(nil)},
|
||||||
out: bytes.NewBuffer(nil),
|
out: bytes.NewBuffer(nil),
|
||||||
ch: make(chan struct{}),
|
ch: make(chan struct{}),
|
||||||
}
|
}
|
||||||
e.cli = NewWithConfig(printLogo,
|
var c config.Config
|
||||||
|
if cfg == nil {
|
||||||
|
configPath := "../../config/protocol.unit_testnet.single.yml"
|
||||||
|
var err error
|
||||||
|
c, err = config.LoadFile(configPath)
|
||||||
|
require.NoError(t, err, "could not load chain config")
|
||||||
|
c.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
|
||||||
|
} else {
|
||||||
|
c = *cfg
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
e.cli, err = NewWithConfig(printLogo,
|
||||||
func(int) { e.exit.Store(true) },
|
func(int) { e.exit.Store(true) },
|
||||||
&readline.Config{
|
&readline.Config{
|
||||||
Prompt: "",
|
Prompt: "",
|
||||||
|
@ -79,10 +100,40 @@ func newTestVMCLIWithLogo(t *testing.T, printLogo bool) *executor {
|
||||||
FuncIsTerminal: func() bool {
|
FuncIsTerminal: func() bool {
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
})
|
}, c)
|
||||||
|
require.NoError(t, err)
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestVMClIWithState(t *testing.T) *executor {
|
||||||
|
// Firstly create a DB with chain, save and close it.
|
||||||
|
path := t.TempDir()
|
||||||
|
opts := dbconfig.LevelDBOptions{
|
||||||
|
DataDirectoryPath: path,
|
||||||
|
}
|
||||||
|
store, err := storage.NewLevelDBStore(opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
customConfig := func(c *config.ProtocolConfiguration) {
|
||||||
|
c.StateRootInHeader = true // Need for P2PStateExchangeExtensions check.
|
||||||
|
c.P2PSigExtensions = true // Need for basic chain initializer.
|
||||||
|
}
|
||||||
|
bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, store)
|
||||||
|
require.NoError(t, err)
|
||||||
|
go bc.Run()
|
||||||
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
|
basicchain.InitSimple(t, "../../", e)
|
||||||
|
bc.Close()
|
||||||
|
|
||||||
|
// After that create VMCLI backed by created chain.
|
||||||
|
configPath := "../../config/protocol.unit_testnet.yml"
|
||||||
|
cfg, err := config.LoadFile(configPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
|
||||||
|
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions = opts
|
||||||
|
cfg.ProtocolConfiguration.StateRootInHeader = true
|
||||||
|
return newTestVMCLIWithLogoAndCustomConfig(t, false, &cfg)
|
||||||
|
}
|
||||||
|
|
||||||
func (e *executor) runProg(t *testing.T, commands ...string) {
|
func (e *executor) runProg(t *testing.T, commands ...string) {
|
||||||
e.runProgWithTimeout(t, 4*time.Second, commands...)
|
e.runProgWithTimeout(t, 4*time.Second, commands...)
|
||||||
}
|
}
|
||||||
|
@ -662,3 +713,18 @@ func TestReset(t *testing.T) {
|
||||||
e.checkNextLine(t, "")
|
e.checkNextLine(t, "")
|
||||||
e.checkError(t, fmt.Errorf("VM is not ready: no program loaded"))
|
e.checkError(t, fmt.Errorf("VM is not ready: no program loaded"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunWithState(t *testing.T) {
|
||||||
|
e := newTestVMClIWithState(t)
|
||||||
|
|
||||||
|
// Ensure that state is properly loaded and on-chain contract can be called.
|
||||||
|
script := io.NewBufBinWriter()
|
||||||
|
h, err := e.cli.chain.GetContractScriptHash(1) // examples/storage/storage.go
|
||||||
|
require.NoError(t, err)
|
||||||
|
emit.AppCall(script.BinWriter, h, "put", callflag.All, 3, 3)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadhex "+hex.EncodeToString(script.Bytes()),
|
||||||
|
"run")
|
||||||
|
e.checkNextLine(t, "READY: loaded 37 instructions")
|
||||||
|
e.checkStack(t, 3)
|
||||||
|
}
|
||||||
|
|
25
cli/vm/vm.go
25
cli/vm/vm.go
|
@ -1,20 +1,25 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||||
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCommands returns 'vm' command.
|
// NewCommands returns 'vm' command.
|
||||||
func NewCommands() []cli.Command {
|
func NewCommands() []cli.Command {
|
||||||
|
cfgFlags := []cli.Flag{options.Config}
|
||||||
|
cfgFlags = append(cfgFlags, options.Network...)
|
||||||
return []cli.Command{{
|
return []cli.Command{{
|
||||||
Name: "vm",
|
Name: "vm",
|
||||||
Usage: "start the virtual machine",
|
Usage: "start the virtual machine",
|
||||||
Action: startVMPrompt,
|
Action: startVMPrompt,
|
||||||
Flags: []cli.Flag{},
|
Flags: cfgFlags,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +27,22 @@ func startVMPrompt(ctx *cli.Context) error {
|
||||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p := NewWithConfig(true, os.Exit, &readline.Config{})
|
|
||||||
|
cfg, err := options.GetConfigFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
if ctx.NumFlags() == 0 {
|
||||||
|
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
|
||||||
|
}
|
||||||
|
if cfg.ApplicationConfiguration.DBConfiguration.Type != dbconfig.InMemoryDB {
|
||||||
|
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.ReadOnly = true
|
||||||
|
cfg.ApplicationConfiguration.DBConfiguration.BoltDBOptions.ReadOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := NewWithConfig(true, os.Exit, &readline.Config{}, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("failed to create VM CLI: %w", err), 1)
|
||||||
|
}
|
||||||
return p.Run()
|
return p.Run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,31 @@ import (
|
||||||
|
|
||||||
const neoAmount = 99999000
|
const neoAmount = 99999000
|
||||||
|
|
||||||
|
// InitSimple initializes chain with single contract storing several storage values.
|
||||||
|
// It's not as complicated as chain got after Init and may be used for tests where
|
||||||
|
// chain with a small amount of data is needed and for historical functionality testing.
|
||||||
|
// Needs a path to the root directory.
|
||||||
|
func InitSimple(t *testing.T, rootpath string, e *neotest.Executor) {
|
||||||
|
// examplesPrefix is a prefix of the example smart-contracts.
|
||||||
|
var examplesPrefix = filepath.Join(rootpath, "examples")
|
||||||
|
|
||||||
|
// Block #1: deploy storage contract (examples/storage/storage.go).
|
||||||
|
_, storageHash := newDeployTx(t, e, e.Validator,
|
||||||
|
filepath.Join(examplesPrefix, "storage", "storage.go"),
|
||||||
|
filepath.Join(examplesPrefix, "storage", "storage.yml"),
|
||||||
|
true)
|
||||||
|
|
||||||
|
// Block #2: put (1, 1) kv pair.
|
||||||
|
storageValidatorInvoker := e.ValidatorInvoker(storageHash)
|
||||||
|
storageValidatorInvoker.Invoke(t, 1, "put", 1, 1)
|
||||||
|
|
||||||
|
// Block #3: put (2, 2) kv pair.
|
||||||
|
storageValidatorInvoker.Invoke(t, 2, "put", 2, 2)
|
||||||
|
|
||||||
|
// Block #4: update (1, 1) -> (1, 2).
|
||||||
|
storageValidatorInvoker.Invoke(t, 1, "put", 1, 2)
|
||||||
|
}
|
||||||
|
|
||||||
// Init pushes some predefined set of transactions into the given chain, it needs a path to
|
// Init pushes some predefined set of transactions into the given chain, it needs a path to
|
||||||
// the root project directory.
|
// the root project directory.
|
||||||
func Init(t *testing.T, rootpath string, e *neotest.Executor) {
|
func Init(t *testing.T, rootpath string, e *neotest.Executor) {
|
||||||
|
|
Loading…
Reference in a new issue