Merge pull request #1108 from nspcc-dev/neo3/compiler/nef

compiler: support *.nef and *.manifest.json generation
This commit is contained in:
Roman Khimov 2020-06-29 20:55:47 +03:00 committed by GitHub
commit 372dd71708
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 654 additions and 147 deletions

View file

@ -16,12 +16,12 @@ import (
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"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/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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/nef"
"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/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
@ -73,14 +73,14 @@ func NewCommands() []cli.Command {
testInvokeScriptFlags := []cli.Flag{ testInvokeScriptFlags := []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "in, i", Name: "in, i",
Usage: "Input location of the avm file that needs to be invoked", Usage: "Input location of the .nef file that needs to be invoked",
}, },
} }
testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...) testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...)
deployFlags := []cli.Flag{ deployFlags := []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "in, i", Name: "in, i",
Usage: "Input file for the smart contract (*.avm)", Usage: "Input file for the smart contract (*.nef)",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "config, c", Name: "config, c",
@ -103,7 +103,7 @@ func NewCommands() []cli.Command {
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "compile", Name: "compile",
Usage: "compile a smart contract to a .avm file", Usage: "compile a smart contract to a .nef file",
Action: contractCompile, Action: contractCompile,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
@ -123,8 +123,8 @@ func NewCommands() []cli.Command {
Usage: "Emit debug info in a separate file", Usage: "Emit debug info in a separate file",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "abi, a", Name: "manifest, m",
Usage: "Emit application binary interface (.abi.json) file into separate file using configuration input file (*.yml)", Usage: "Emit contract manifest (*.manifest.json) file into separate file using configuration input file (*.yml)",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "config, c", Name: "config, c",
@ -134,7 +134,7 @@ func NewCommands() []cli.Command {
}, },
{ {
Name: "deploy", Name: "deploy",
Usage: "deploy a smart contract (.avm with description)", Usage: "deploy a smart contract (.nef with description)",
Description: `Deploys given contract into the chain. The gas parameter is for additional Description: `Deploys given contract into the chain. The gas parameter is for additional
gas to be added as a network fee to prioritize the transaction. It may also gas to be added as a network fee to prioritize the transaction. It may also
be required to add that to satisfy chain's policy regarding transaction size be required to add that to satisfy chain's policy regarding transaction size
@ -254,9 +254,9 @@ func NewCommands() []cli.Command {
}, },
{ {
Name: "testinvokescript", Name: "testinvokescript",
Usage: "Invoke compiled AVM code on the blockchain (test mode, not creating a transaction for it)", Usage: "Invoke compiled AVM code in NEF format on the blockchain (test mode, not creating a transaction for it)",
UsageText: "neo-go contract testinvokescript -r endpoint -i input.avm [cosigners...]", UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [cosigners...]",
Description: `Executes given compiled AVM instructions with the given set of Description: `Executes given compiled AVM instructions in NEF format with the given set of
cosigners. See testinvokefunction documentation for the details about parameters. cosigners. See testinvokefunction documentation for the details about parameters.
`, `,
Action: testInvokeScript, Action: testInvokeScript,
@ -288,7 +288,7 @@ func NewCommands() []cli.Command {
}, },
cli.StringFlag{ cli.StringFlag{
Name: "in, i", Name: "in, i",
Usage: "input file of the program", Usage: "input file of the program (either .go or .nef)",
}, },
}, },
}, },
@ -349,17 +349,17 @@ func contractCompile(ctx *cli.Context) error {
if len(src) == 0 { if len(src) == 0 {
return cli.NewExitError(errNoInput, 1) return cli.NewExitError(errNoInput, 1)
} }
abi := ctx.String("abi") manifestFile := ctx.String("manifest")
confFile := ctx.String("config") confFile := ctx.String("config")
if len(abi) != 0 && len(confFile) == 0 { if len(manifestFile) != 0 && len(confFile) == 0 {
return cli.NewExitError(errNoConfFile, 1) return cli.NewExitError(errNoConfFile, 1)
} }
o := &compiler.Options{ o := &compiler.Options{
Outfile: ctx.String("out"), Outfile: ctx.String("out"),
DebugInfo: ctx.String("debug"), DebugInfo: ctx.String("debug"),
ABIInfo: abi, ManifestFile: manifestFile,
} }
if len(confFile) != 0 { if len(confFile) != 0 {
@ -492,6 +492,10 @@ func testInvokeScript(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
nefFile, err := nef.FileFromBytes(b)
if err != nil {
return cli.NewExitError(errors.Wrapf(err, "failed to restore .nef file"), 1)
}
args := ctx.Args() args := ctx.Args()
var cosigners []transaction.Cosigner var cosigners []transaction.Cosigner
@ -513,7 +517,7 @@ func testInvokeScript(ctx *cli.Context) error {
return err return err
} }
scriptHex := hex.EncodeToString(b) scriptHex := hex.EncodeToString(nefFile.Script)
resp, err := c.InvokeScript(scriptHex, cosigners) resp, err := c.InvokeScript(scriptHex, cosigners)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
@ -551,11 +555,10 @@ func (p *ProjectConfig) GetFeatures() smartcontract.PropertyState {
} }
// ToManifest converts project config to the manifest. // ToManifest converts project config to the manifest.
func (p *ProjectConfig) ToManifest(script []byte) *manifest.Manifest { func (p *ProjectConfig) ToManifest(file nef.File) *manifest.Manifest {
h := hash.Hash160(script) m := manifest.NewManifest(file.Header.ScriptHash)
m := manifest.NewManifest(h)
m.Features = p.GetFeatures() m.Features = p.GetFeatures()
m.ABI.Hash = h m.ABI.Hash = file.Header.ScriptHash
m.ABI.EntryPoint = p.EntryPoint m.ABI.EntryPoint = p.EntryPoint
m.ABI.Methods = p.Methods m.ABI.Methods = p.Methods
m.ABI.Events = p.Events m.ABI.Events = p.Events
@ -577,6 +580,12 @@ func inspect(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(errors.Wrap(err, "failed to compile"), 1) return cli.NewExitError(errors.Wrap(err, "failed to compile"), 1)
} }
} else {
nefFile, err := nef.FileFromBytes(b)
if err != nil {
return cli.NewExitError(errors.Wrapf(err, "failed to restore .nef file"), 1)
}
b = nefFile.Script
} }
v := vm.New() v := vm.New()
v.LoadScript(b) v.LoadScript(b)
@ -638,10 +647,14 @@ func contractDeploy(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
avm, err := ioutil.ReadFile(in) f, err := ioutil.ReadFile(in)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
nefFile, err := nef.FileFromBytes(f)
if err != nil {
return cli.NewExitError(errors.Wrapf(err, "failed to restore .nef file"), 1)
}
conf, err := parseContractConfig(confFile) conf, err := parseContractConfig(confFile)
if err != nil { if err != nil {
return err return err
@ -655,8 +668,8 @@ func contractDeploy(ctx *cli.Context) error {
return err return err
} }
m := conf.ToManifest(avm) m := conf.ToManifest(nefFile)
txScript, sysfee, err := request.CreateDeploymentScript(avm, m) txScript, sysfee, err := request.CreateDeploymentScript(nefFile.Script, m)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1) return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1)
} }
@ -665,7 +678,7 @@ func contractDeploy(ctx *cli.Context) error {
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1) return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
} }
fmt.Printf("Sent deployment transaction %s for contract %s\n", txHash.StringLE(), hash.Hash160(avm).StringLE()) fmt.Printf("Sent deployment transaction %s for contract %s\n", txHash.StringLE(), nefFile.Header.ScriptHash.StringLE())
return nil return nil
} }

View file

@ -104,11 +104,11 @@ In case you don't want to provide details use `--skip-details, -skip`.
./bin/neo-go contract compile -i mycontract.go ./bin/neo-go contract compile -i mycontract.go
``` ```
By default the output filename will be the name of your `.go` file with the `.avm` extension, the file will be located By default the output filename will be the name of your `.go` file with the `.nef` extension, the file will be located
in the same directory where you called the command from. If you want another location for your compiled contract: in the same directory where you called the command from. If you want another location for your compiled contract:
``` ```
./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.avm ./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.nef
``` ```
### Deploy ### Deploy
@ -118,7 +118,7 @@ in the same directory where you called the command from. If you want another loc
//Implemented in test mode. It means that it won't affect the blockchain //Implemented in test mode. It means that it won't affect the blockchain
``` ```
./bin/neo-go contract testinvoke -i mycontract.avm ./bin/neo-go contract testinvoke -i mycontract.nef
``` ```
### Debug ### Debug

View file

@ -37,17 +37,17 @@ functionality as Neo .net Framework library.
./bin/neo-go contract compile -i mycontract.go ./bin/neo-go contract compile -i mycontract.go
``` ```
By default the filename will be the name of your .go file with the .avm extension, the file will be located in the same directory where your Go contract is. If you want another location for your compiled contract: By default the filename will be the name of your .go file with the .nef extension, the file will be located in the same directory where your Go contract is. If you want another location for your compiled contract:
``` ```
./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.avm ./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.nef
``` ```
### Debugging ### Debugging
You can dump the opcodes generated by the compiler with the following command: You can dump the opcodes generated by the compiler with the following command:
``` ```
./bin/neo-go contract inspect -i mycontract.go ./bin/neo-go contract inspect -i mycontract.go -c
``` ```
This will result in something like this: This will result in something like this:
@ -116,7 +116,7 @@ Toolkit](https://github.com/neo-project/neo-blockchain-toolkit/). To do that
you need to generate debug information using `--debug` option, like this: you need to generate debug information using `--debug` option, like this:
``` ```
$ ./bin/neo-go contract compile -i contract.go -o contract.avm --debug contract.debug.json $ ./bin/neo-go contract compile -i contract.go -o contract.nef --debug contract.debug.json
``` ```
This file can then be used by debugger and set up to work just like for any This file can then be used by debugger and set up to work just like for any
@ -144,7 +144,7 @@ project:
It's passed to the `deploy` command via `-c` option: It's passed to the `deploy` command via `-c` option:
``` ```
$ ./bin/neo-go contract deploy -i contract.avm -c contract.yml -r http://localhost:20331 -w wallet.json -g 0.001 $ ./bin/neo-go contract deploy -i contract.nef -c contract.yml -r http://localhost:20331 -w wallet.json -g 0.001
``` ```
Deployment works via an RPC server, an address of which is passed via `-r` Deployment works via an RPC server, an address of which is passed via `-r`
@ -163,7 +163,7 @@ deployment with neo-go. It's done in the same step with compilation via
support the command line will look like this: support the command line will look like this:
``` ```
$ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.avm --debug contract.debug.json --abi contract.abi.json $ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.nef --debug contract.debug.json --abi contract.abi.json
``` ```
This file can then be used by toolkit to deploy contract the same way This file can then be used by toolkit to deploy contract the same way

View file

@ -1,6 +1,6 @@
# NEO-GO-VM # NEO-GO-VM
A cross platform virtual machine implementation for `avm` compatible programs. A cross platform virtual machine implementation for `NEF` compatible programs.
# Installation # Installation
@ -46,7 +46,7 @@ Commands:
help display help help display help
ip Show current instruction ip Show current instruction
istack Show invocation stack contents istack Show invocation stack contents
loadavm Load an avm script into the VM loadnef Load an avm script in NEF format into the VM
loadgo Compile and load a Go file into the VM loadgo Compile and load a Go file into the VM
loadhex Load a hex-encoded script string into the VM loadhex Load a hex-encoded script string into the VM
ops Dump opcodes of the current loaded program ops Dump opcodes of the current loaded program
@ -70,10 +70,10 @@ Usage: step [<n>]
## Loading in your script ## Loading in your script
To load an avm script into the VM: To load an avm script in NEF format into the VM:
``` ```
NEO-GO-VM > loadavm ../contract.avm NEO-GO-VM > loadnef ../contract.nef
READY: loaded 36 instructions READY: loaded 36 instructions
``` ```

View file

@ -12,14 +12,15 @@ import (
"strings" "strings"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
) )
const fileExt = "avm" const fileExt = "nef"
// Options contains all the parameters that affect the behaviour of the compiler. // Options contains all the parameters that affect the behaviour of the compiler.
type Options struct { type Options struct {
// The extension of the output file default set to .avm // The extension of the output file default set to .nef
Ext string Ext string
// The name of the output file. // The name of the output file.
@ -28,8 +29,8 @@ type Options struct {
// The name of the output for debug info. // The name of the output for debug info.
DebugInfo string DebugInfo string
// The name of the output for application binary interface info. // The name of the output for contract manifest file.
ABIInfo string ManifestFile string
// Contract metadata. // Contract metadata.
ContractFeatures smartcontract.PropertyState ContractFeatures smartcontract.PropertyState
@ -78,7 +79,7 @@ func CompileWithDebugInfo(r io.Reader) ([]byte, *DebugInfo, error) {
return CodeGen(ctx) return CodeGen(ctx)
} }
// CompileAndSave will compile and save the file to disk. // CompileAndSave will compile and save the file to disk in the NEF format.
func CompileAndSave(src string, o *Options) ([]byte, error) { func CompileAndSave(src string, o *Options) ([]byte, error) {
if !strings.HasSuffix(src, ".go") { if !strings.HasSuffix(src, ".go") {
return nil, fmt.Errorf("%s is not a Go file", src) return nil, fmt.Errorf("%s is not a Go file", src)
@ -98,11 +99,22 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("error while trying to compile smart contract file: %v", err) return nil, fmt.Errorf("error while trying to compile smart contract file: %v", err)
} }
f, err := nef.NewFile(b)
if err != nil {
return nil, fmt.Errorf("error while trying to create .nef file: %v", err)
}
bytes, err := f.Bytes()
if err != nil {
return nil, fmt.Errorf("error while serializing .nef file: %v", err)
}
out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext) out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext)
err = ioutil.WriteFile(out, b, os.ModePerm) err = ioutil.WriteFile(out, bytes, os.ModePerm)
if o.DebugInfo == "" { if err != nil {
return b, err return b, err
} }
if o.DebugInfo == "" {
return b, nil
}
p, err := filepath.Abs(src) p, err := filepath.Abs(src)
if err != nil { if err != nil {
return b, err return b, err
@ -115,13 +127,16 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
if err := ioutil.WriteFile(o.DebugInfo, data, os.ModePerm); err != nil { if err := ioutil.WriteFile(o.DebugInfo, data, os.ModePerm); err != nil {
return b, err return b, err
} }
if o.ABIInfo == "" { if o.ManifestFile == "" {
return b, err return b, err
} }
abi := di.convertToABI(o.ContractFeatures) m, err := di.convertToManifest(o.ContractFeatures)
abiData, err := json.Marshal(abi)
if err != nil { if err != nil {
return b, err return b, err
} }
return b, ioutil.WriteFile(o.ABIInfo, abiData, os.ModePerm) mData, err := json.Marshal(m)
if err != nil {
return b, err
}
return b, ioutil.WriteFile(o.ManifestFile, mData, os.ModePerm)
} }

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
"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/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -20,6 +21,8 @@ type compilerTestCase struct {
} }
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
// CompileAndSave use config.Version for proper .nef generation.
config.Version = "0.90.0-test"
testCases := []compilerTestCase{ testCases := []compilerTestCase{
{ {
name: "TestCompile", name: "TestCompile",
@ -44,7 +47,7 @@ func TestCompiler(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
err = os.MkdirAll(exampleSavePath, os.ModePerm) err = os.MkdirAll(exampleSavePath, os.ModePerm)
require.NoError(t, err) require.NoError(t, err)
outfile := exampleSavePath + "/test.avm" outfile := exampleSavePath + "/test.nef"
_, err = compiler.CompileAndSave(exampleCompilePath+"/"+infos[0].Name(), &compiler.Options{Outfile: outfile}) _, err = compiler.CompileAndSave(exampleCompilePath+"/"+infos[0].Name(), &compiler.Options{Outfile: outfile})
require.NoError(t, err) require.NoError(t, err)
defer func() { defer func() {

View file

@ -11,6 +11,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
) )
@ -32,7 +33,7 @@ type MethodDebugInfo struct {
// Parameters is a list of method's parameters. // Parameters is a list of method's parameters.
Parameters []DebugParam `json:"params"` Parameters []DebugParam `json:"params"`
// ReturnType is method's return type. // ReturnType is method's return type.
ReturnType string `json:"return-type"` ReturnType string `json:"return"`
Variables []string `json:"variables"` Variables []string `json:"variables"`
// SeqPoints is a map between source lines and byte-code instruction offsets. // SeqPoints is a map between source lines and byte-code instruction offsets.
SeqPoints []DebugSeqPoint `json:"sequence-points"` SeqPoints []DebugSeqPoint `json:"sequence-points"`
@ -49,7 +50,7 @@ type EventDebugInfo struct {
ID string `json:"id"` ID string `json:"id"`
// Name is a human-readable event name in a format "{namespace}-{name}". // Name is a human-readable event name in a format "{namespace}-{name}".
Name string `json:"name"` Name string `json:"name"`
Parameters []DebugParam `json:"parameters"` Parameters []DebugParam `json:"params"`
} }
// DebugSeqPoint represents break-point for debugger. // DebugSeqPoint represents break-point for debugger.
@ -80,40 +81,6 @@ type DebugParam struct {
Type string `json:"type"` Type string `json:"type"`
} }
// ABI represents ABI contract info in compatible with NEO Blockchain Toolkit format
type ABI struct {
Hash util.Uint160 `json:"hash"`
Metadata Metadata `json:"metadata"`
EntryPoint string `json:"entrypoint"`
Functions []Method `json:"functions"`
Events []Event `json:"events"`
}
// Metadata represents ABI contract metadata
type Metadata struct {
Author string `json:"author"`
Email string `json:"email"`
Version string `json:"version"`
Title string `json:"title"`
Description string `json:"description"`
HasStorage bool `json:"has-storage"`
HasDynamicInvocation bool `json:"has-dynamic-invoke"`
IsPayable bool `json:"is-payable"`
}
// Method represents ABI method's metadata.
type Method struct {
Name string `json:"name"`
Parameters []DebugParam `json:"parameters"`
ReturnType string `json:"returntype"`
}
// Event represents ABI event's metadata.
type Event struct {
Name string `json:"name"`
Parameters []DebugParam `json:"parameters"`
}
func (c *codegen) saveSequencePoint(n ast.Node) { func (c *codegen) saveSequencePoint(n ast.Node) {
if c.scope == nil { if c.scope == nil {
// do not save globals for now // do not save globals for now
@ -267,6 +234,59 @@ func (d *DebugParam) UnmarshalJSON(data []byte) error {
return nil return nil
} }
// ToManifestParameter converts DebugParam to manifest.Parameter
func (d *DebugParam) ToManifestParameter() (manifest.Parameter, error) {
pType, err := smartcontract.ParseParamType(d.Type)
if err != nil {
return manifest.Parameter{}, err
}
return manifest.Parameter{
Name: d.Name,
Type: pType,
}, nil
}
// ToManifestMethod converts MethodDebugInfo to manifest.Method
func (m *MethodDebugInfo) ToManifestMethod() (manifest.Method, error) {
var (
result manifest.Method
err error
)
parameters := make([]manifest.Parameter, len(m.Parameters))
for i, p := range m.Parameters {
parameters[i], err = p.ToManifestParameter()
if err != nil {
return result, err
}
}
returnType, err := smartcontract.ParseParamType(m.ReturnType)
if err != nil {
return result, err
}
result.Name = m.Name.Name
result.Parameters = parameters
result.ReturnType = returnType
return result, nil
}
// ToManifestEvent converts EventDebugInfo to manifest.Event
func (e *EventDebugInfo) ToManifestEvent() (manifest.Event, error) {
var (
result manifest.Event
err error
)
parameters := make([]manifest.Parameter, len(e.Parameters))
for i, p := range e.Parameters {
parameters[i], err = p.ToManifestParameter()
if err != nil {
return result, err
}
}
result.Name = e.Name
result.Parameters = parameters
return result, nil
}
// MarshalJSON implements json.Marshaler interface. // MarshalJSON implements json.Marshaler interface.
func (d *DebugMethodName) MarshalJSON() ([]byte, error) { func (d *DebugMethodName) MarshalJSON() ([]byte, error) {
return []byte(`"` + d.Namespace + `,` + d.Name + `"`), nil return []byte(`"` + d.Namespace + `,` + d.Name + `"`), nil
@ -311,35 +331,58 @@ func parsePairJSON(data []byte, sep string) (string, string, error) {
return ss[0], ss[1], nil return ss[0], ss[1], nil
} }
// convertToABI converts contract to the ABI struct for debugger. // convertToManifest converts contract to the manifest.Manifest struct for debugger.
// Note: manifest is taken from the external source, however it can be generated ad-hoc. See #1038. // Note: manifest is taken from the external source, however it can be generated ad-hoc. See #1038.
func (di *DebugInfo) convertToABI(fs smartcontract.PropertyState) ABI { func (di *DebugInfo) convertToManifest(fs smartcontract.PropertyState) (*manifest.Manifest, error) {
methods := make([]Method, 0) var (
entryPoint manifest.Method
err error
)
for _, method := range di.Methods { for _, method := range di.Methods {
if method.Name.Name == mainIdent { if method.Name.Name == mainIdent {
methods = append(methods, Method{ entryPoint, err = method.ToManifestMethod()
Name: method.Name.Name, if err != nil {
Parameters: method.Parameters, return nil, err
ReturnType: method.ReturnType, }
})
break break
} }
} }
events := make([]Event, len(di.Events)) if entryPoint.Name == "" {
for i, event := range di.Events { return nil, errors.New("no Main method was found")
events[i] = Event{ }
Name: event.Name, methods := make([]manifest.Method, 0, len(di.Methods)-1)
Parameters: event.Parameters, for _, method := range di.Methods {
if method.Name.Name != mainIdent {
mMethod, err := method.ToManifestMethod()
if err != nil {
return nil, err
}
methods = append(methods, mMethod)
} }
} }
return ABI{ events := make([]manifest.Event, len(di.Events))
Hash: di.Hash, for i, event := range di.Events {
Metadata: Metadata{ events[i], err = event.ToManifestEvent()
HasStorage: fs&smartcontract.HasStorage != 0, if err != nil {
IsPayable: fs&smartcontract.IsPayable != 0, return nil, err
}, }
EntryPoint: mainIdent, }
Functions: methods,
result := manifest.NewManifest(di.Hash)
result.Features = fs
result.ABI = manifest.ABI{
Hash: di.Hash,
EntryPoint: entryPoint,
Methods: methods,
Events: events, Events: events,
} }
result.Permissions = []manifest.Permission{
{
Contract: manifest.PermissionDesc{
Type: manifest.PermissionWildcard,
},
Methods: manifest.WildStrings{},
},
}
return result, nil
} }

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -121,31 +122,84 @@ func methodStruct() struct{} { return struct{}{} }
require.EqualValues(t, opcode.RET, buf[index]) require.EqualValues(t, opcode.RET, buf[index])
} }
t.Run("convert to ABI", func(t *testing.T) { t.Run("convert to Manifest", func(t *testing.T) {
actual := d.convertToABI(smartcontract.HasStorage) actual, err := d.convertToManifest(smartcontract.HasStorage)
expected := ABI{ require.NoError(t, err)
Hash: hash.Hash160(buf), expected := &manifest.Manifest{
Metadata: Metadata{ ABI: manifest.ABI{
HasStorage: true, Hash: hash.Hash160(buf),
HasDynamicInvocation: false, EntryPoint: manifest.Method{
IsPayable: false, Name: "Main",
}, Parameters: []manifest.Parameter{
EntryPoint: mainIdent,
Functions: []Method{
{
Name: mainIdent,
Parameters: []DebugParam{
{ {
Name: "op", Name: "op",
Type: "String", Type: smartcontract.StringType,
}, },
}, },
ReturnType: "Boolean", ReturnType: smartcontract.BoolType,
},
Methods: []manifest.Method{
{
Name: "methodInt",
Parameters: []manifest.Parameter{
{
Name: "a",
Type: smartcontract.StringType,
},
},
ReturnType: smartcontract.IntegerType,
},
{
Name: "methodString",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.StringType,
},
{
Name: "methodByteArray",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.ByteArrayType,
},
{
Name: "methodArray",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.ArrayType,
},
{
Name: "methodStruct",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.ArrayType,
},
},
Events: []manifest.Event{},
},
Groups: []manifest.Group{},
Features: smartcontract.HasStorage,
Permissions: []manifest.Permission{
{
Contract: manifest.PermissionDesc{
Type: manifest.PermissionWildcard,
},
Methods: manifest.WildStrings{},
}, },
}, },
Events: []Event{}, Trusts: manifest.WildUint160s{
Value: []util.Uint160{},
},
SafeMethods: manifest.WildStrings{
Value: []string{},
},
Extra: nil,
} }
assert.Equal(t, expected, actual) require.True(t, expected.ABI.Hash.Equals(actual.ABI.Hash))
require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods)
require.Equal(t, expected.ABI.EntryPoint, actual.ABI.EntryPoint)
require.Equal(t, expected.ABI.Events, actual.ABI.Events)
require.Equal(t, expected.Groups, actual.Groups)
require.Equal(t, expected.Features, actual.Features)
require.Equal(t, expected.Permissions, actual.Permissions)
require.Equal(t, expected.Trusts, actual.Trusts)
require.Equal(t, expected.SafeMethods, actual.SafeMethods)
require.Equal(t, expected.Extra, actual.Extra)
}) })
} }

View file

@ -6,7 +6,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/network/metrics" "github.com/nspcc-dev/neo-go/pkg/network/metrics"
"github.com/nspcc-dev/neo-go/pkg/rpc" "github.com/nspcc-dev/neo-go/pkg/rpc"
"github.com/nspcc-dev/neo-go/pkg/wallet"
) )
// ApplicationConfiguration config specific to the node. // ApplicationConfiguration config specific to the node.
@ -26,5 +25,5 @@ type ApplicationConfiguration struct {
ProtoTickInterval time.Duration `yaml:"ProtoTickInterval"` ProtoTickInterval time.Duration `yaml:"ProtoTickInterval"`
Relay bool `yaml:"Relay"` Relay bool `yaml:"Relay"`
RPC rpc.Config `yaml:"RPC"` RPC rpc.Config `yaml:"RPC"`
UnlockWallet wallet.Config `yaml:"UnlockWallet"` UnlockWallet Wallet `yaml:"UnlockWallet"`
} }

View file

@ -1,7 +1,7 @@
package wallet package config
// Config is a wallet info. // Wallet is a wallet info.
type Config struct { type Wallet struct {
Path string `yaml:"Path"` Path string `yaml:"Path"`
Password string `yaml:"Password"` Password string `yaml:"Password"`
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/dbft/block" "github.com/nspcc-dev/dbft/block"
"github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/crypto"
"github.com/nspcc-dev/dbft/payload" "github.com/nspcc-dev/dbft/payload"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
coreb "github.com/nspcc-dev/neo-go/pkg/core/block" coreb "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
@ -84,7 +85,7 @@ type Config struct {
// TimePerBlock minimal time that should pass before next block is accepted. // TimePerBlock minimal time that should pass before next block is accepted.
TimePerBlock time.Duration TimePerBlock time.Duration
// Wallet is a local-node wallet configuration. // Wallet is a local-node wallet configuration.
Wallet *wallet.Config Wallet *config.Wallet
} }
// NewService returns new consensus.Service instance. // NewService returns new consensus.Service instance.

View file

@ -18,7 +18,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
) )
@ -216,7 +215,7 @@ func newTestService(t *testing.T) *service {
Broadcast: func(*Payload) {}, Broadcast: func(*Payload) {},
Chain: newTestChain(t), Chain: newTestChain(t),
RequestTx: func(...util.Uint256) {}, RequestTx: func(...util.Uint256) {},
Wallet: &wallet.Config{ Wallet: &config.Wallet{
Path: "./testdata/wallet1.json", Path: "./testdata/wallet1.json",
Password: "one", Password: "one",
}, },

View file

@ -5,7 +5,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
) )
@ -63,7 +62,7 @@ type (
LogLevel zapcore.Level LogLevel zapcore.Level
// Wallet is a wallet configuration. // Wallet is a wallet configuration.
Wallet *wallet.Config Wallet *config.Wallet
// TimePerBlock is an interval which should pass between two successive blocks. // TimePerBlock is an interval which should pass between two successive blocks.
TimePerBlock time.Duration TimePerBlock time.Duration
@ -76,7 +75,7 @@ func NewServerConfig(cfg config.Config) ServerConfig {
appConfig := cfg.ApplicationConfiguration appConfig := cfg.ApplicationConfiguration
protoConfig := cfg.ProtocolConfiguration protoConfig := cfg.ProtocolConfiguration
var wc *wallet.Config var wc *config.Wallet
if appConfig.UnlockWallet.Path != "" { if appConfig.UnlockWallet.Path != "" {
wc = &appConfig.UnlockWallet wc = &appConfig.UnlockWallet
} }

View file

@ -0,0 +1,236 @@
package nef
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"strconv"
"strings"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// NEO Executable Format 3 (NEF3)
// Standard: https://github.com/neo-project/proposals/pull/121/files
// Implementation: https://github.com/neo-project/neo/blob/v3.0.0-preview2/src/neo/SmartContract/NefFile.cs#L8
// +------------+-----------+------------------------------------------------------------+
// | Field | Length | Comment |
// +------------+-----------+------------------------------------------------------------+
// | Magic | 4 bytes | Magic header |
// | Compiler | 32 bytes | Compiler used |
// | Version | 16 bytes | Compiler version (Major, Minor, Build, Version) |
// | ScriptHash | 20 bytes | ScriptHash for the script (BE) |
// +------------+-----------+------------------------------------------------------------+
// | Checksum | 4 bytes | Sha256 of the header (CRC) |
// +------------+-----------+------------------------------------------------------------+
// | Script | Var bytes | Var bytes for the payload |
// +------------+-----------+------------------------------------------------------------+
const (
// Magic is a magic File header constant.
Magic uint32 = 0x3346454E
// MaxScriptLength is the maximum allowed contract script length.
MaxScriptLength = 1024 * 1024
// compilerFieldSize is the length of `Compiler` File header field in bytes.
compilerFieldSize = 32
)
// File represents compiled contract file structure according to the NEF3 standard.
type File struct {
Header Header
Checksum uint32
Script []byte
}
// Header represents File header.
type Header struct {
Magic uint32
Compiler string
Version Version
ScriptHash util.Uint160
}
// Version represents compiler version.
type Version struct {
Major int32
Minor int32
Build int32
Revision int32
}
// NewFile returns new NEF3 file with script specified.
func NewFile(script []byte) (File, error) {
file := File{
Header: Header{
Magic: Magic,
Compiler: "neo-go",
ScriptHash: hash.Hash160(script),
},
Script: script,
}
v, err := GetVersion(config.Version)
if err != nil {
return file, err
}
file.Header.Version = v
file.Checksum = file.Header.CalculateChecksum()
return file, nil
}
// GetVersion returns Version from the given string. It accepts the following formats:
// `major.minor.build-[...]`
// `major.minor.build.revision-[...]`
// where `major`, `minor`, `build` and `revision` are 32-bit integers with base=10
func GetVersion(version string) (Version, error) {
var (
result Version
)
versions := strings.SplitN(version, ".", 4)
if len(versions) < 3 {
return result, errors.New("invalid version format")
}
major, err := strconv.ParseInt(versions[0], 10, 32)
if err != nil {
return result, fmt.Errorf("failed to parse major version: %v", err)
}
result.Major = int32(major)
minor, err := strconv.ParseInt(versions[1], 10, 32)
if err != nil {
return result, fmt.Errorf("failed to parse minor version: %v", err)
}
result.Minor = int32(minor)
b := versions[2]
if len(versions) == 3 {
b = strings.SplitN(b, "-", 2)[0]
}
build, err := strconv.ParseInt(b, 10, 32)
if err != nil {
return result, fmt.Errorf("failed to parse build version: %v", err)
}
result.Build = int32(build)
if len(versions) == 4 {
r := strings.SplitN(versions[3], "-", 2)[0]
revision, err := strconv.ParseInt(r, 10, 32)
if err != nil {
return result, fmt.Errorf("failed to parse revision version: %v", err)
}
result.Revision = int32(revision)
}
return result, nil
}
// EncodeBinary implements io.Serializable interface.
func (v *Version) EncodeBinary(w *io.BinWriter) {
w.WriteU32LE(uint32(v.Major))
w.WriteU32LE(uint32(v.Minor))
w.WriteU32LE(uint32(v.Build))
w.WriteU32LE(uint32(v.Revision))
}
// DecodeBinary implements io.Serializable interface.
func (v *Version) DecodeBinary(r *io.BinReader) {
v.Major = int32(r.ReadU32LE())
v.Minor = int32(r.ReadU32LE())
v.Build = int32(r.ReadU32LE())
v.Revision = int32(r.ReadU32LE())
}
// EncodeBinary implements io.Serializable interface.
func (h *Header) EncodeBinary(w *io.BinWriter) {
w.WriteU32LE(h.Magic)
if len(h.Compiler) > compilerFieldSize {
w.Err = errors.New("invalid compiler name length")
return
}
bytes := []byte(h.Compiler)
w.WriteBytes(bytes)
if len(bytes) < compilerFieldSize {
w.WriteBytes(make([]byte, compilerFieldSize-len(bytes)))
}
h.Version.EncodeBinary(w)
h.ScriptHash.EncodeBinary(w)
}
// DecodeBinary implements io.Serializable interface.
func (h *Header) DecodeBinary(r *io.BinReader) {
h.Magic = r.ReadU32LE()
if h.Magic != Magic {
r.Err = errors.New("invalid Magic")
return
}
buf := make([]byte, compilerFieldSize)
r.ReadBytes(buf)
buf = bytes.TrimRightFunc(buf, func(r rune) bool {
return r == 0
})
h.Compiler = string(buf)
h.Version.DecodeBinary(r)
h.ScriptHash.DecodeBinary(r)
}
// CalculateChecksum returns first 4 bytes of SHA256(Header) converted to uint32.
func (h *Header) CalculateChecksum() uint32 {
buf := io.NewBufBinWriter()
h.EncodeBinary(buf.BinWriter)
if buf.Err != nil {
panic(buf.Err)
}
return binary.LittleEndian.Uint32(hash.Sha256(buf.Bytes()).BytesBE())
}
// EncodeBinary implements io.Serializable interface.
func (n *File) EncodeBinary(w *io.BinWriter) {
n.Header.EncodeBinary(w)
w.WriteU32LE(n.Checksum)
w.WriteVarBytes(n.Script)
}
// DecodeBinary implements io.Serializable interface.
func (n *File) DecodeBinary(r *io.BinReader) {
n.Header.DecodeBinary(r)
n.Checksum = r.ReadU32LE()
checksum := n.Header.CalculateChecksum()
if checksum != n.Checksum {
r.Err = errors.New("CRC verification fail")
return
}
n.Script = r.ReadVarBytes()
if len(n.Script) > MaxScriptLength {
r.Err = errors.New("invalid script length")
return
}
if !hash.Hash160(n.Script).Equals(n.Header.ScriptHash) {
r.Err = errors.New("script hashes mismatch")
return
}
}
// Bytes returns byte array with serialized NEF File.
func (n File) Bytes() ([]byte, error) {
buf := io.NewBufBinWriter()
n.EncodeBinary(buf.BinWriter)
if buf.Err != nil {
return nil, buf.Err
}
return buf.Bytes(), nil
}
// FileFromBytes returns NEF File deserialized from given bytes.
func FileFromBytes(source []byte) (File, error) {
result := File{}
r := io.NewBinReaderFromBuf(source)
result.DecodeBinary(r)
if r.Err != nil {
return result, r.Err
}
return result, nil
}

View file

@ -0,0 +1,140 @@
package nef
import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestEncodeDecodeBinary(t *testing.T) {
script := []byte{12, 32, 84, 35, 14}
expected := &File{
Header: Header{
Magic: Magic,
Compiler: "the best compiler ever",
Version: Version{
Major: 1,
Minor: 2,
Build: 3,
Revision: 4,
},
ScriptHash: hash.Hash160(script),
},
Script: script,
}
t.Run("invalid Magic", func(t *testing.T) {
expected.Header.Magic = 123
checkDecodeError(t, expected)
})
t.Run("invalid checksum", func(t *testing.T) {
expected.Header.Magic = Magic
expected.Checksum = 123
checkDecodeError(t, expected)
})
t.Run("invalid script length", func(t *testing.T) {
newScript := make([]byte, MaxScriptLength+1)
expected.Script = newScript
expected.Header.ScriptHash = hash.Hash160(newScript)
expected.Checksum = expected.Header.CalculateChecksum()
checkDecodeError(t, expected)
})
t.Run("invalid scripthash", func(t *testing.T) {
expected.Script = script
expected.Header.ScriptHash = util.Uint160{1, 2, 3}
expected.Checksum = expected.Header.CalculateChecksum()
checkDecodeError(t, expected)
})
t.Run("positive", func(t *testing.T) {
expected.Script = script
expected.Header.ScriptHash = hash.Hash160(script)
expected.Checksum = expected.Header.CalculateChecksum()
expected.Header.Magic = Magic
testserdes.EncodeDecodeBinary(t, expected, &File{})
})
}
func checkDecodeError(t *testing.T, expected *File) {
bytes, err := testserdes.EncodeBinary(expected)
require.NoError(t, err)
require.Error(t, testserdes.DecodeBinary(bytes, &File{}))
}
func TestBytesFromBytes(t *testing.T) {
script := []byte{12, 32, 84, 35, 14}
expected := File{
Header: Header{
Magic: Magic,
Compiler: "the best compiler ever",
Version: Version{
Major: 1,
Minor: 2,
Build: 3,
Revision: 4,
},
ScriptHash: hash.Hash160(script),
},
Script: script,
}
expected.Checksum = expected.Header.CalculateChecksum()
bytes, err := expected.Bytes()
require.NoError(t, err)
actual, err := FileFromBytes(bytes)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
func TestGetVersion(t *testing.T) {
_, err := GetVersion("qwerty")
require.Error(t, err)
_, err = GetVersion("1.pre")
require.Error(t, err)
_, err = GetVersion("1.1.pre")
require.Error(t, err)
_, err = GetVersion("1.1.1.pre")
require.Error(t, err)
actual, err := GetVersion("1.1.1-pre")
require.NoError(t, err)
expected := Version{
Major: 1,
Minor: 1,
Build: 1,
Revision: 0,
}
require.Equal(t, expected, actual)
actual, err = GetVersion("0.90.0-pre")
require.NoError(t, err)
expected = Version{
Major: 0,
Minor: 90,
Build: 0,
Revision: 0,
}
require.Equal(t, expected, actual)
actual, err = GetVersion("1.1.1.1-pre")
require.NoError(t, err)
expected = Version{
Major: 1,
Minor: 1,
Build: 1,
Revision: 1,
}
require.Equal(t, expected, actual)
_, err = GetVersion("1.1.1.1.1")
require.Error(t, err)
}

View file

@ -67,12 +67,12 @@ var commands = []*ishell.Cmd{
Func: handleXStack, Func: handleXStack,
}, },
{ {
Name: "loadavm", Name: "loadnef",
Help: "Load an avm script into the VM", Help: "Load a NEF-consistent script into the VM",
LongHelp: `Usage: loadavm <file> LongHelp: `Usage: loadnef <file>
<file> is mandatory parameter, example: <file> is mandatory parameter, example:
> load /path/to/script.avm`, > loadnef /path/to/script.nef`,
Func: handleLoadAVM, Func: handleLoadNEF,
}, },
{ {
Name: "loadbase64", Name: "loadbase64",
@ -87,15 +87,15 @@ var commands = []*ishell.Cmd{
Help: "Load a hex-encoded script string into the VM", Help: "Load a hex-encoded script string into the VM",
LongHelp: `Usage: loadhex <string> LongHelp: `Usage: loadhex <string>
<string> is mandatory parameter, example: <string> is mandatory parameter, example:
> load 0c0c48656c6c6f20776f726c6421`, > loadhex 0c0c48656c6c6f20776f726c6421`,
Func: handleLoadHex, Func: handleLoadHex,
}, },
{ {
Name: "loadgo", Name: "loadgo",
Help: "Compile and load a Go file into the VM", Help: "Compile and load a Go file into the VM",
LongHelp: `Usage: loadhex <file> LongHelp: `Usage: loadgo <file>
<file> is mandatory parameter, example: <file> is mandatory parameter, example:
> load /path/to/file.go`, > loadgo /path/to/file.go`,
Func: handleLoadGo, Func: handleLoadGo,
}, },
{ {
@ -241,7 +241,7 @@ func handleXStack(c *ishell.Context) {
c.Println(v.Stack(c.Cmd.Name)) c.Println(v.Stack(c.Cmd.Name))
} }
func handleLoadAVM(c *ishell.Context) { func handleLoadNEF(c *ishell.Context) {
v := getVMFromContext(c) v := getVMFromContext(c)
if len(c.Args) < 1 { if len(c.Args) < 1 {
c.Err(errors.New("missing parameter <file>")) c.Err(errors.New("missing parameter <file>"))

View file

@ -14,6 +14,7 @@ import (
"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/bigint" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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/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/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
@ -86,7 +87,7 @@ type VM struct {
keys map[string]*keys.PublicKey keys map[string]*keys.PublicKey
} }
// New returns a new VM object ready to load .avm bytecode scripts. // New returns a new VM object ready to load AVM bytecode scripts.
func New() *VM { func New() *VM {
return NewWithTrigger(trigger.System) return NewWithTrigger(trigger.System)
} }
@ -248,13 +249,17 @@ func (v *VM) AddBreakPointRel(n int) {
v.AddBreakPoint(ctx.ip + n) v.AddBreakPoint(ctx.ip + n)
} }
// LoadFile loads a program from the given path, ready to execute it. // LoadFile loads a program in NEF format from the given path, ready to execute it.
func (v *VM) LoadFile(path string) error { func (v *VM) LoadFile(path string) error {
b, err := ioutil.ReadFile(path) b, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return err return err
} }
v.Load(b) f, err := nef.FileFromBytes(b)
if err != nil {
return err
}
v.Load(f.Script)
return nil return nil
} }