forked from TrueCloudLab/neoneo-go
compiler, cli: introduce *.manifest.json
Add ability to generate NEO3-compatable *.manifest.json into compiler. This file represets contract manifest and includes ABI information, so we don't need to create separate *.abi.json file. NEO3 debugger also needs *.manifest.json only. So, switched from *.abi.json to *.manifest.json file.
This commit is contained in:
parent
1c1818d97e
commit
2f6065f541
4 changed files with 175 additions and 50 deletions
|
@ -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",
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -29,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
|
||||||
|
@ -124,13 +124,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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -267,6 +268,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 +365,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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,29 +122,73 @@ 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,
|
||||||
}
|
}
|
||||||
require.True(t, expected.ABI.Hash.Equals(actual.ABI.Hash))
|
require.True(t, expected.ABI.Hash.Equals(actual.ABI.Hash))
|
||||||
require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods)
|
require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods)
|
||||||
|
|
Loading…
Reference in a new issue